diff --git a/.cache/clangd/index/Application.cpp.8776A52D61132E96.idx b/.cache/clangd/index/Application.cpp.8776A52D61132E96.idx new file mode 100644 index 0000000..d6ce93d Binary files /dev/null and b/.cache/clangd/index/Application.cpp.8776A52D61132E96.idx differ diff --git a/.cache/clangd/index/Application.h.858A603E426F648A.idx b/.cache/clangd/index/Application.h.858A603E426F648A.idx new file mode 100644 index 0000000..7008153 Binary files /dev/null and b/.cache/clangd/index/Application.h.858A603E426F648A.idx differ diff --git a/.cache/clangd/index/DrawBindings.cpp.D3B812DF8CB26330.idx b/.cache/clangd/index/DrawBindings.cpp.D3B812DF8CB26330.idx new file mode 100644 index 0000000..048ef0b Binary files /dev/null and b/.cache/clangd/index/DrawBindings.cpp.D3B812DF8CB26330.idx differ diff --git a/.cache/clangd/index/DrawBindings.h.CC7314AB1A121981.idx b/.cache/clangd/index/DrawBindings.h.CC7314AB1A121981.idx new file mode 100644 index 0000000..f78989b Binary files /dev/null and b/.cache/clangd/index/DrawBindings.h.CC7314AB1A121981.idx differ diff --git a/.cache/clangd/index/FA6FreeSolidFontData.h.FC535BAFFB56AA07.idx b/.cache/clangd/index/FA6FreeSolidFontData.h.FC535BAFFB56AA07.idx new file mode 100644 index 0000000..930455e Binary files /dev/null and b/.cache/clangd/index/FA6FreeSolidFontData.h.FC535BAFFB56AA07.idx differ diff --git a/.cache/clangd/index/GuiManager.cpp.673B708E1204267E.idx b/.cache/clangd/index/GuiManager.cpp.673B708E1204267E.idx new file mode 100644 index 0000000..f728696 Binary files /dev/null and b/.cache/clangd/index/GuiManager.cpp.673B708E1204267E.idx differ diff --git a/.cache/clangd/index/GuiManager.h.BF6B9FB11B7598EE.idx b/.cache/clangd/index/GuiManager.h.BF6B9FB11B7598EE.idx new file mode 100644 index 0000000..0f8ba16 Binary files /dev/null and b/.cache/clangd/index/GuiManager.h.BF6B9FB11B7598EE.idx differ diff --git a/.cache/clangd/index/HotReload.cpp.F47033D97042FC77.idx b/.cache/clangd/index/HotReload.cpp.F47033D97042FC77.idx new file mode 100644 index 0000000..d54147d Binary files /dev/null and b/.cache/clangd/index/HotReload.cpp.F47033D97042FC77.idx differ diff --git a/.cache/clangd/index/HotReload.h.E78A1E604B731DD0.idx b/.cache/clangd/index/HotReload.h.E78A1E604B731DD0.idx new file mode 100644 index 0000000..4a7cac9 Binary files /dev/null and b/.cache/clangd/index/HotReload.h.E78A1E604B731DD0.idx differ diff --git a/.cache/clangd/index/IconsFontAwesome6.h.4F690858433DCB54.idx b/.cache/clangd/index/IconsFontAwesome6.h.4F690858433DCB54.idx new file mode 100644 index 0000000..11219d6 Binary files /dev/null and b/.cache/clangd/index/IconsFontAwesome6.h.4F690858433DCB54.idx differ diff --git a/.cache/clangd/index/ImGuiNotify.hpp.EE306DC3E81DBD8B.idx b/.cache/clangd/index/ImGuiNotify.hpp.EE306DC3E81DBD8B.idx new file mode 100644 index 0000000..4a66abf Binary files /dev/null and b/.cache/clangd/index/ImGuiNotify.hpp.EE306DC3E81DBD8B.idx differ diff --git a/.cache/clangd/index/ImageBindings.cpp.106301A4847C5051.idx b/.cache/clangd/index/ImageBindings.cpp.106301A4847C5051.idx new file mode 100644 index 0000000..4250c8f Binary files /dev/null and b/.cache/clangd/index/ImageBindings.cpp.106301A4847C5051.idx differ diff --git a/.cache/clangd/index/ImageBindings.h.67795D26AAFCD1E4.idx b/.cache/clangd/index/ImageBindings.h.67795D26AAFCD1E4.idx new file mode 100644 index 0000000..76eb505 Binary files /dev/null and b/.cache/clangd/index/ImageBindings.h.67795D26AAFCD1E4.idx differ diff --git a/.cache/clangd/index/LogBindings.cpp.F216E8762748E2D1.idx b/.cache/clangd/index/LogBindings.cpp.F216E8762748E2D1.idx new file mode 100644 index 0000000..624fc45 Binary files /dev/null and b/.cache/clangd/index/LogBindings.cpp.F216E8762748E2D1.idx differ diff --git a/.cache/clangd/index/LogBindings.h.03A2A0BF520F958E.idx b/.cache/clangd/index/LogBindings.h.03A2A0BF520F958E.idx new file mode 100644 index 0000000..647214f Binary files /dev/null and b/.cache/clangd/index/LogBindings.h.03A2A0BF520F958E.idx differ diff --git a/.cache/clangd/index/LogWindow.cpp.EAFA4FFD61920E45.idx b/.cache/clangd/index/LogWindow.cpp.EAFA4FFD61920E45.idx new file mode 100644 index 0000000..3b3992c Binary files /dev/null and b/.cache/clangd/index/LogWindow.cpp.EAFA4FFD61920E45.idx differ diff --git a/.cache/clangd/index/LogWindow.h.739C84098534FA87.idx b/.cache/clangd/index/LogWindow.h.739C84098534FA87.idx new file mode 100644 index 0000000..af936e5 Binary files /dev/null and b/.cache/clangd/index/LogWindow.h.739C84098534FA87.idx differ diff --git a/.cache/clangd/index/MathBindings.cpp.A3EA272C10D858CA.idx b/.cache/clangd/index/MathBindings.cpp.A3EA272C10D858CA.idx new file mode 100644 index 0000000..722ed51 Binary files /dev/null and b/.cache/clangd/index/MathBindings.cpp.A3EA272C10D858CA.idx differ diff --git a/.cache/clangd/index/MathBindings.h.4EB245298CE82877.idx b/.cache/clangd/index/MathBindings.h.4EB245298CE82877.idx new file mode 100644 index 0000000..7e5aaaa Binary files /dev/null and b/.cache/clangd/index/MathBindings.h.4EB245298CE82877.idx differ diff --git a/.cache/clangd/index/ScriptBindings.cpp.E23B554865E2DC43.idx b/.cache/clangd/index/ScriptBindings.cpp.E23B554865E2DC43.idx new file mode 100644 index 0000000..4bbef71 Binary files /dev/null and b/.cache/clangd/index/ScriptBindings.cpp.E23B554865E2DC43.idx differ diff --git a/.cache/clangd/index/ScriptBindings.h.1767B8F57F307012.idx b/.cache/clangd/index/ScriptBindings.h.1767B8F57F307012.idx new file mode 100644 index 0000000..766d371 Binary files /dev/null and b/.cache/clangd/index/ScriptBindings.h.1767B8F57F307012.idx differ diff --git a/.cache/clangd/index/ScriptEngine.cpp.7ACDEA59EB4DE303.idx b/.cache/clangd/index/ScriptEngine.cpp.7ACDEA59EB4DE303.idx new file mode 100644 index 0000000..a0c552b Binary files /dev/null and b/.cache/clangd/index/ScriptEngine.cpp.7ACDEA59EB4DE303.idx differ diff --git a/.cache/clangd/index/ScriptEngine.h.E0931BF4ECAC1BB0.idx b/.cache/clangd/index/ScriptEngine.h.E0931BF4ECAC1BB0.idx new file mode 100644 index 0000000..8ac4879 Binary files /dev/null and b/.cache/clangd/index/ScriptEngine.h.E0931BF4ECAC1BB0.idx differ diff --git a/.cache/clangd/index/TextureBindings.cpp.ED92828A1ED5C540.idx b/.cache/clangd/index/TextureBindings.cpp.ED92828A1ED5C540.idx new file mode 100644 index 0000000..b2392b9 Binary files /dev/null and b/.cache/clangd/index/TextureBindings.cpp.ED92828A1ED5C540.idx differ diff --git a/.cache/clangd/index/TextureBindings.h.4E98F904F8826D78.idx b/.cache/clangd/index/TextureBindings.h.4E98F904F8826D78.idx new file mode 100644 index 0000000..5225cb0 Binary files /dev/null and b/.cache/clangd/index/TextureBindings.h.4E98F904F8826D78.idx differ diff --git a/.cache/clangd/index/Toast.h.7992F998C96CE2CA.idx b/.cache/clangd/index/Toast.h.7992F998C96CE2CA.idx new file mode 100644 index 0000000..027e8d2 Binary files /dev/null and b/.cache/clangd/index/Toast.h.7992F998C96CE2CA.idx differ diff --git a/.cache/clangd/index/ToastBindings.cpp.02CE1E8043BDE66D.idx b/.cache/clangd/index/ToastBindings.cpp.02CE1E8043BDE66D.idx new file mode 100644 index 0000000..a6684d3 Binary files /dev/null and b/.cache/clangd/index/ToastBindings.cpp.02CE1E8043BDE66D.idx differ diff --git a/.cache/clangd/index/ToastBindings.h.61AE191277D79513.idx b/.cache/clangd/index/ToastBindings.h.61AE191277D79513.idx new file mode 100644 index 0000000..2d9e0c0 Binary files /dev/null and b/.cache/clangd/index/ToastBindings.h.61AE191277D79513.idx differ diff --git a/.cache/clangd/index/acutest.h.0D2F8C30EC1B66E0.idx b/.cache/clangd/index/acutest.h.0D2F8C30EC1B66E0.idx new file mode 100644 index 0000000..17a197e Binary files /dev/null and b/.cache/clangd/index/acutest.h.0D2F8C30EC1B66E0.idx differ diff --git a/.cache/clangd/index/angelscript.h.AC475AED505975DD.idx b/.cache/clangd/index/angelscript.h.AC475AED505975DD.idx new file mode 100644 index 0000000..2a84ba2 Binary files /dev/null and b/.cache/clangd/index/angelscript.h.AC475AED505975DD.idx differ diff --git a/.cache/clangd/index/as_array.h.B06B657A6FAA5011.idx b/.cache/clangd/index/as_array.h.B06B657A6FAA5011.idx new file mode 100644 index 0000000..e5c4537 Binary files /dev/null and b/.cache/clangd/index/as_array.h.B06B657A6FAA5011.idx differ diff --git a/.cache/clangd/index/as_atomic.cpp.D441A68A9330007D.idx b/.cache/clangd/index/as_atomic.cpp.D441A68A9330007D.idx new file mode 100644 index 0000000..a2e325a Binary files /dev/null and b/.cache/clangd/index/as_atomic.cpp.D441A68A9330007D.idx differ diff --git a/.cache/clangd/index/as_atomic.h.98FF439E721B3419.idx b/.cache/clangd/index/as_atomic.h.98FF439E721B3419.idx new file mode 100644 index 0000000..674b6a8 Binary files /dev/null and b/.cache/clangd/index/as_atomic.h.98FF439E721B3419.idx differ diff --git a/.cache/clangd/index/as_builder.cpp.5B991AB7F848169A.idx b/.cache/clangd/index/as_builder.cpp.5B991AB7F848169A.idx new file mode 100644 index 0000000..92055c7 Binary files /dev/null and b/.cache/clangd/index/as_builder.cpp.5B991AB7F848169A.idx differ diff --git a/.cache/clangd/index/as_builder.h.00D1D5DB84CC6772.idx b/.cache/clangd/index/as_builder.h.00D1D5DB84CC6772.idx new file mode 100644 index 0000000..8de502a Binary files /dev/null and b/.cache/clangd/index/as_builder.h.00D1D5DB84CC6772.idx differ diff --git a/.cache/clangd/index/as_bytecode.cpp.71E2F83E289CB227.idx b/.cache/clangd/index/as_bytecode.cpp.71E2F83E289CB227.idx new file mode 100644 index 0000000..2b4b416 Binary files /dev/null and b/.cache/clangd/index/as_bytecode.cpp.71E2F83E289CB227.idx differ diff --git a/.cache/clangd/index/as_bytecode.h.CA7F431C9B25F8E1.idx b/.cache/clangd/index/as_bytecode.h.CA7F431C9B25F8E1.idx new file mode 100644 index 0000000..c1aab31 Binary files /dev/null and b/.cache/clangd/index/as_bytecode.h.CA7F431C9B25F8E1.idx differ diff --git a/.cache/clangd/index/as_callfunc.cpp.C0CF031BC9B3F5F4.idx b/.cache/clangd/index/as_callfunc.cpp.C0CF031BC9B3F5F4.idx new file mode 100644 index 0000000..b226e01 Binary files /dev/null and b/.cache/clangd/index/as_callfunc.cpp.C0CF031BC9B3F5F4.idx differ diff --git a/.cache/clangd/index/as_callfunc.h.B7EBA57BCC1841B1.idx b/.cache/clangd/index/as_callfunc.h.B7EBA57BCC1841B1.idx new file mode 100644 index 0000000..f7285f2 Binary files /dev/null and b/.cache/clangd/index/as_callfunc.h.B7EBA57BCC1841B1.idx differ diff --git a/.cache/clangd/index/as_callfunc_mips.cpp.F43F7C77711AC923.idx b/.cache/clangd/index/as_callfunc_mips.cpp.F43F7C77711AC923.idx new file mode 100644 index 0000000..a0db172 Binary files /dev/null and b/.cache/clangd/index/as_callfunc_mips.cpp.F43F7C77711AC923.idx differ diff --git a/.cache/clangd/index/as_callfunc_x64_gcc.cpp.2B9B6E753A40B5C5.idx b/.cache/clangd/index/as_callfunc_x64_gcc.cpp.2B9B6E753A40B5C5.idx new file mode 100644 index 0000000..a266ab6 Binary files /dev/null and b/.cache/clangd/index/as_callfunc_x64_gcc.cpp.2B9B6E753A40B5C5.idx differ diff --git a/.cache/clangd/index/as_callfunc_x64_mingw.cpp.02C3DF7B496086F1.idx b/.cache/clangd/index/as_callfunc_x64_mingw.cpp.02C3DF7B496086F1.idx new file mode 100644 index 0000000..d6a28fb Binary files /dev/null and b/.cache/clangd/index/as_callfunc_x64_mingw.cpp.02C3DF7B496086F1.idx differ diff --git a/.cache/clangd/index/as_callfunc_x64_msvc.cpp.1A0494FBF3190501.idx b/.cache/clangd/index/as_callfunc_x64_msvc.cpp.1A0494FBF3190501.idx new file mode 100644 index 0000000..e1412fe Binary files /dev/null and b/.cache/clangd/index/as_callfunc_x64_msvc.cpp.1A0494FBF3190501.idx differ diff --git a/.cache/clangd/index/as_callfunc_x86.cpp.A0DEAD74858CF856.idx b/.cache/clangd/index/as_callfunc_x86.cpp.A0DEAD74858CF856.idx new file mode 100644 index 0000000..cb6f92d Binary files /dev/null and b/.cache/clangd/index/as_callfunc_x86.cpp.A0DEAD74858CF856.idx differ diff --git a/.cache/clangd/index/as_compiler.cpp.FB22BDBD390751F2.idx b/.cache/clangd/index/as_compiler.cpp.FB22BDBD390751F2.idx new file mode 100644 index 0000000..c6514d6 Binary files /dev/null and b/.cache/clangd/index/as_compiler.cpp.FB22BDBD390751F2.idx differ diff --git a/.cache/clangd/index/as_compiler.h.113AEE4326224489.idx b/.cache/clangd/index/as_compiler.h.113AEE4326224489.idx new file mode 100644 index 0000000..5e91c8e Binary files /dev/null and b/.cache/clangd/index/as_compiler.h.113AEE4326224489.idx differ diff --git a/.cache/clangd/index/as_config.h.0D6663DCAC5041EA.idx b/.cache/clangd/index/as_config.h.0D6663DCAC5041EA.idx new file mode 100644 index 0000000..c69bc2f Binary files /dev/null and b/.cache/clangd/index/as_config.h.0D6663DCAC5041EA.idx differ diff --git a/.cache/clangd/index/as_configgroup.cpp.1978D5FE841E12B2.idx b/.cache/clangd/index/as_configgroup.cpp.1978D5FE841E12B2.idx new file mode 100644 index 0000000..5ff4ed8 Binary files /dev/null and b/.cache/clangd/index/as_configgroup.cpp.1978D5FE841E12B2.idx differ diff --git a/.cache/clangd/index/as_configgroup.h.47769F873BE0B41A.idx b/.cache/clangd/index/as_configgroup.h.47769F873BE0B41A.idx new file mode 100644 index 0000000..d860bb9 Binary files /dev/null and b/.cache/clangd/index/as_configgroup.h.47769F873BE0B41A.idx differ diff --git a/.cache/clangd/index/as_context.cpp.E3B08CC2931C1E25.idx b/.cache/clangd/index/as_context.cpp.E3B08CC2931C1E25.idx new file mode 100644 index 0000000..c5208d5 Binary files /dev/null and b/.cache/clangd/index/as_context.cpp.E3B08CC2931C1E25.idx differ diff --git a/.cache/clangd/index/as_context.h.47F3D2D0DA8CC748.idx b/.cache/clangd/index/as_context.h.47F3D2D0DA8CC748.idx new file mode 100644 index 0000000..0017c61 Binary files /dev/null and b/.cache/clangd/index/as_context.h.47F3D2D0DA8CC748.idx differ diff --git a/.cache/clangd/index/as_criticalsection.h.130F097169824D1C.idx b/.cache/clangd/index/as_criticalsection.h.130F097169824D1C.idx new file mode 100644 index 0000000..bd7cddf Binary files /dev/null and b/.cache/clangd/index/as_criticalsection.h.130F097169824D1C.idx differ diff --git a/.cache/clangd/index/as_datatype.cpp.E0A62FE00E11D0A1.idx b/.cache/clangd/index/as_datatype.cpp.E0A62FE00E11D0A1.idx new file mode 100644 index 0000000..c4fd413 Binary files /dev/null and b/.cache/clangd/index/as_datatype.cpp.E0A62FE00E11D0A1.idx differ diff --git a/.cache/clangd/index/as_datatype.h.31D59544BB09D8E7.idx b/.cache/clangd/index/as_datatype.h.31D59544BB09D8E7.idx new file mode 100644 index 0000000..4f77818 Binary files /dev/null and b/.cache/clangd/index/as_datatype.h.31D59544BB09D8E7.idx differ diff --git a/.cache/clangd/index/as_debug.h.527CACBD7491503F.idx b/.cache/clangd/index/as_debug.h.527CACBD7491503F.idx new file mode 100644 index 0000000..8c7d77b Binary files /dev/null and b/.cache/clangd/index/as_debug.h.527CACBD7491503F.idx differ diff --git a/.cache/clangd/index/as_gc.cpp.AC20E1A61B2CBCFB.idx b/.cache/clangd/index/as_gc.cpp.AC20E1A61B2CBCFB.idx new file mode 100644 index 0000000..49247eb Binary files /dev/null and b/.cache/clangd/index/as_gc.cpp.AC20E1A61B2CBCFB.idx differ diff --git a/.cache/clangd/index/as_gc.h.99128ED2E571142C.idx b/.cache/clangd/index/as_gc.h.99128ED2E571142C.idx new file mode 100644 index 0000000..e133f83 Binary files /dev/null and b/.cache/clangd/index/as_gc.h.99128ED2E571142C.idx differ diff --git a/.cache/clangd/index/as_generic.cpp.C80CCE51026AAE64.idx b/.cache/clangd/index/as_generic.cpp.C80CCE51026AAE64.idx new file mode 100644 index 0000000..2683a3e Binary files /dev/null and b/.cache/clangd/index/as_generic.cpp.C80CCE51026AAE64.idx differ diff --git a/.cache/clangd/index/as_generic.h.53B17A96528B62ED.idx b/.cache/clangd/index/as_generic.h.53B17A96528B62ED.idx new file mode 100644 index 0000000..0fb377e Binary files /dev/null and b/.cache/clangd/index/as_generic.h.53B17A96528B62ED.idx differ diff --git a/.cache/clangd/index/as_globalproperty.cpp.E4E70F1E3BDD257D.idx b/.cache/clangd/index/as_globalproperty.cpp.E4E70F1E3BDD257D.idx new file mode 100644 index 0000000..4be4530 Binary files /dev/null and b/.cache/clangd/index/as_globalproperty.cpp.E4E70F1E3BDD257D.idx differ diff --git a/.cache/clangd/index/as_map.h.A3AD2B90FEEB726F.idx b/.cache/clangd/index/as_map.h.A3AD2B90FEEB726F.idx new file mode 100644 index 0000000..7f32949 Binary files /dev/null and b/.cache/clangd/index/as_map.h.A3AD2B90FEEB726F.idx differ diff --git a/.cache/clangd/index/as_memory.cpp.552DF5AD14ECC4B5.idx b/.cache/clangd/index/as_memory.cpp.552DF5AD14ECC4B5.idx new file mode 100644 index 0000000..ec2ca1b Binary files /dev/null and b/.cache/clangd/index/as_memory.cpp.552DF5AD14ECC4B5.idx differ diff --git a/.cache/clangd/index/as_memory.h.C6EFA7F35F05C98D.idx b/.cache/clangd/index/as_memory.h.C6EFA7F35F05C98D.idx new file mode 100644 index 0000000..3511338 Binary files /dev/null and b/.cache/clangd/index/as_memory.h.C6EFA7F35F05C98D.idx differ diff --git a/.cache/clangd/index/as_module.cpp.0B1C5EFB053A59F6.idx b/.cache/clangd/index/as_module.cpp.0B1C5EFB053A59F6.idx new file mode 100644 index 0000000..c03af85 Binary files /dev/null and b/.cache/clangd/index/as_module.cpp.0B1C5EFB053A59F6.idx differ diff --git a/.cache/clangd/index/as_module.h.D7D335828B947862.idx b/.cache/clangd/index/as_module.h.D7D335828B947862.idx new file mode 100644 index 0000000..1023055 Binary files /dev/null and b/.cache/clangd/index/as_module.h.D7D335828B947862.idx differ diff --git a/.cache/clangd/index/as_namespace.h.F7B71193DB11DB1A.idx b/.cache/clangd/index/as_namespace.h.F7B71193DB11DB1A.idx new file mode 100644 index 0000000..fe9f752 Binary files /dev/null and b/.cache/clangd/index/as_namespace.h.F7B71193DB11DB1A.idx differ diff --git a/.cache/clangd/index/as_objecttype.cpp.F06C6FB1C9AED6FA.idx b/.cache/clangd/index/as_objecttype.cpp.F06C6FB1C9AED6FA.idx new file mode 100644 index 0000000..44393c0 Binary files /dev/null and b/.cache/clangd/index/as_objecttype.cpp.F06C6FB1C9AED6FA.idx differ diff --git a/.cache/clangd/index/as_objecttype.h.785FD4622CEC3E36.idx b/.cache/clangd/index/as_objecttype.h.785FD4622CEC3E36.idx new file mode 100644 index 0000000..85c75fb Binary files /dev/null and b/.cache/clangd/index/as_objecttype.h.785FD4622CEC3E36.idx differ diff --git a/.cache/clangd/index/as_outputbuffer.cpp.B6366FB27E3B19A7.idx b/.cache/clangd/index/as_outputbuffer.cpp.B6366FB27E3B19A7.idx new file mode 100644 index 0000000..97b1ede Binary files /dev/null and b/.cache/clangd/index/as_outputbuffer.cpp.B6366FB27E3B19A7.idx differ diff --git a/.cache/clangd/index/as_outputbuffer.h.ACD41D21BD4DC007.idx b/.cache/clangd/index/as_outputbuffer.h.ACD41D21BD4DC007.idx new file mode 100644 index 0000000..f268408 Binary files /dev/null and b/.cache/clangd/index/as_outputbuffer.h.ACD41D21BD4DC007.idx differ diff --git a/.cache/clangd/index/as_parser.cpp.BD2BBF497B7C08D9.idx b/.cache/clangd/index/as_parser.cpp.BD2BBF497B7C08D9.idx new file mode 100644 index 0000000..2a32b34 Binary files /dev/null and b/.cache/clangd/index/as_parser.cpp.BD2BBF497B7C08D9.idx differ diff --git a/.cache/clangd/index/as_parser.h.835A6072918E81C2.idx b/.cache/clangd/index/as_parser.h.835A6072918E81C2.idx new file mode 100644 index 0000000..4880846 Binary files /dev/null and b/.cache/clangd/index/as_parser.h.835A6072918E81C2.idx differ diff --git a/.cache/clangd/index/as_property.h.B4B9AAB7616A0D6B.idx b/.cache/clangd/index/as_property.h.B4B9AAB7616A0D6B.idx new file mode 100644 index 0000000..d25db2c Binary files /dev/null and b/.cache/clangd/index/as_property.h.B4B9AAB7616A0D6B.idx differ diff --git a/.cache/clangd/index/as_restore.cpp.914EC6C72AA277A3.idx b/.cache/clangd/index/as_restore.cpp.914EC6C72AA277A3.idx new file mode 100644 index 0000000..22e1c15 Binary files /dev/null and b/.cache/clangd/index/as_restore.cpp.914EC6C72AA277A3.idx differ diff --git a/.cache/clangd/index/as_restore.h.C7B523ACCDD6860D.idx b/.cache/clangd/index/as_restore.h.C7B523ACCDD6860D.idx new file mode 100644 index 0000000..b9a04af Binary files /dev/null and b/.cache/clangd/index/as_restore.h.C7B523ACCDD6860D.idx differ diff --git a/.cache/clangd/index/as_scriptcode.cpp.785735990E5C292F.idx b/.cache/clangd/index/as_scriptcode.cpp.785735990E5C292F.idx new file mode 100644 index 0000000..1312339 Binary files /dev/null and b/.cache/clangd/index/as_scriptcode.cpp.785735990E5C292F.idx differ diff --git a/.cache/clangd/index/as_scriptcode.h.A66639055A43BA36.idx b/.cache/clangd/index/as_scriptcode.h.A66639055A43BA36.idx new file mode 100644 index 0000000..a20f59b Binary files /dev/null and b/.cache/clangd/index/as_scriptcode.h.A66639055A43BA36.idx differ diff --git a/.cache/clangd/index/as_scriptengine.cpp.811C05404051D15E.idx b/.cache/clangd/index/as_scriptengine.cpp.811C05404051D15E.idx new file mode 100644 index 0000000..9bd13c3 Binary files /dev/null and b/.cache/clangd/index/as_scriptengine.cpp.811C05404051D15E.idx differ diff --git a/.cache/clangd/index/as_scriptengine.h.04C2C814432DD8AF.idx b/.cache/clangd/index/as_scriptengine.h.04C2C814432DD8AF.idx new file mode 100644 index 0000000..5aa2608 Binary files /dev/null and b/.cache/clangd/index/as_scriptengine.h.04C2C814432DD8AF.idx differ diff --git a/.cache/clangd/index/as_scriptfunction.cpp.F9B4DD1EC0FC8DDF.idx b/.cache/clangd/index/as_scriptfunction.cpp.F9B4DD1EC0FC8DDF.idx new file mode 100644 index 0000000..183a80e Binary files /dev/null and b/.cache/clangd/index/as_scriptfunction.cpp.F9B4DD1EC0FC8DDF.idx differ diff --git a/.cache/clangd/index/as_scriptfunction.h.98772B58CB0B667F.idx b/.cache/clangd/index/as_scriptfunction.h.98772B58CB0B667F.idx new file mode 100644 index 0000000..f9e568e Binary files /dev/null and b/.cache/clangd/index/as_scriptfunction.h.98772B58CB0B667F.idx differ diff --git a/.cache/clangd/index/as_scriptnode.cpp.FE66C402D41629AC.idx b/.cache/clangd/index/as_scriptnode.cpp.FE66C402D41629AC.idx new file mode 100644 index 0000000..bc1d077 Binary files /dev/null and b/.cache/clangd/index/as_scriptnode.cpp.FE66C402D41629AC.idx differ diff --git a/.cache/clangd/index/as_scriptnode.h.46E65245C1B8E45F.idx b/.cache/clangd/index/as_scriptnode.h.46E65245C1B8E45F.idx new file mode 100644 index 0000000..e85c249 Binary files /dev/null and b/.cache/clangd/index/as_scriptnode.h.46E65245C1B8E45F.idx differ diff --git a/.cache/clangd/index/as_scriptobject.cpp.74A647D3C5D1F2CA.idx b/.cache/clangd/index/as_scriptobject.cpp.74A647D3C5D1F2CA.idx new file mode 100644 index 0000000..62333ca Binary files /dev/null and b/.cache/clangd/index/as_scriptobject.cpp.74A647D3C5D1F2CA.idx differ diff --git a/.cache/clangd/index/as_scriptobject.h.9025F12F2C738108.idx b/.cache/clangd/index/as_scriptobject.h.9025F12F2C738108.idx new file mode 100644 index 0000000..851a513 Binary files /dev/null and b/.cache/clangd/index/as_scriptobject.h.9025F12F2C738108.idx differ diff --git a/.cache/clangd/index/as_string.cpp.5255D8D36DAECD5C.idx b/.cache/clangd/index/as_string.cpp.5255D8D36DAECD5C.idx new file mode 100644 index 0000000..0efa708 Binary files /dev/null and b/.cache/clangd/index/as_string.cpp.5255D8D36DAECD5C.idx differ diff --git a/.cache/clangd/index/as_string.h.D36854A487979607.idx b/.cache/clangd/index/as_string.h.D36854A487979607.idx new file mode 100644 index 0000000..84a2d8f Binary files /dev/null and b/.cache/clangd/index/as_string.h.D36854A487979607.idx differ diff --git a/.cache/clangd/index/as_string_util.cpp.ED3BDC32F8DD540E.idx b/.cache/clangd/index/as_string_util.cpp.ED3BDC32F8DD540E.idx new file mode 100644 index 0000000..9117dcc Binary files /dev/null and b/.cache/clangd/index/as_string_util.cpp.ED3BDC32F8DD540E.idx differ diff --git a/.cache/clangd/index/as_string_util.h.9A317F1475C17B9C.idx b/.cache/clangd/index/as_string_util.h.9A317F1475C17B9C.idx new file mode 100644 index 0000000..d2da630 Binary files /dev/null and b/.cache/clangd/index/as_string_util.h.9A317F1475C17B9C.idx differ diff --git a/.cache/clangd/index/as_symboltable.h.C39A118BD2A60C0C.idx b/.cache/clangd/index/as_symboltable.h.C39A118BD2A60C0C.idx new file mode 100644 index 0000000..7d60d92 Binary files /dev/null and b/.cache/clangd/index/as_symboltable.h.C39A118BD2A60C0C.idx differ diff --git a/.cache/clangd/index/as_texts.h.A2EC1EC85F68DC9C.idx b/.cache/clangd/index/as_texts.h.A2EC1EC85F68DC9C.idx new file mode 100644 index 0000000..2498251 Binary files /dev/null and b/.cache/clangd/index/as_texts.h.A2EC1EC85F68DC9C.idx differ diff --git a/.cache/clangd/index/as_thread.cpp.16533C75DDD387A2.idx b/.cache/clangd/index/as_thread.cpp.16533C75DDD387A2.idx new file mode 100644 index 0000000..436221a Binary files /dev/null and b/.cache/clangd/index/as_thread.cpp.16533C75DDD387A2.idx differ diff --git a/.cache/clangd/index/as_thread.h.6E3159A81260B7DC.idx b/.cache/clangd/index/as_thread.h.6E3159A81260B7DC.idx new file mode 100644 index 0000000..f6d7e0b Binary files /dev/null and b/.cache/clangd/index/as_thread.h.6E3159A81260B7DC.idx differ diff --git a/.cache/clangd/index/as_tokendef.h.DA624DCE97276FDA.idx b/.cache/clangd/index/as_tokendef.h.DA624DCE97276FDA.idx new file mode 100644 index 0000000..c93042e Binary files /dev/null and b/.cache/clangd/index/as_tokendef.h.DA624DCE97276FDA.idx differ diff --git a/.cache/clangd/index/as_tokenizer.cpp.061A182A21E94D51.idx b/.cache/clangd/index/as_tokenizer.cpp.061A182A21E94D51.idx new file mode 100644 index 0000000..b25ec7c Binary files /dev/null and b/.cache/clangd/index/as_tokenizer.cpp.061A182A21E94D51.idx differ diff --git a/.cache/clangd/index/as_tokenizer.h.854B9CD5479ED89E.idx b/.cache/clangd/index/as_tokenizer.h.854B9CD5479ED89E.idx new file mode 100644 index 0000000..8caa5b5 Binary files /dev/null and b/.cache/clangd/index/as_tokenizer.h.854B9CD5479ED89E.idx differ diff --git a/.cache/clangd/index/as_typeinfo.cpp.8CEF89F6C7B86D66.idx b/.cache/clangd/index/as_typeinfo.cpp.8CEF89F6C7B86D66.idx new file mode 100644 index 0000000..66fe3d8 Binary files /dev/null and b/.cache/clangd/index/as_typeinfo.cpp.8CEF89F6C7B86D66.idx differ diff --git a/.cache/clangd/index/as_typeinfo.h.A4B06C7A534C616F.idx b/.cache/clangd/index/as_typeinfo.h.A4B06C7A534C616F.idx new file mode 100644 index 0000000..921afce Binary files /dev/null and b/.cache/clangd/index/as_typeinfo.h.A4B06C7A534C616F.idx differ diff --git a/.cache/clangd/index/as_variablescope.cpp.A21DCD03EB5BCAC2.idx b/.cache/clangd/index/as_variablescope.cpp.A21DCD03EB5BCAC2.idx new file mode 100644 index 0000000..949878a Binary files /dev/null and b/.cache/clangd/index/as_variablescope.cpp.A21DCD03EB5BCAC2.idx differ diff --git a/.cache/clangd/index/as_variablescope.h.333E3E56495AE2F3.idx b/.cache/clangd/index/as_variablescope.h.333E3E56495AE2F3.idx new file mode 100644 index 0000000..a91d08b Binary files /dev/null and b/.cache/clangd/index/as_variablescope.h.333E3E56495AE2F3.idx differ diff --git a/.cache/clangd/index/cgltf.h.13D0453510CF284B.idx b/.cache/clangd/index/cgltf.h.13D0453510CF284B.idx new file mode 100644 index 0000000..4d9691a Binary files /dev/null and b/.cache/clangd/index/cgltf.h.13D0453510CF284B.idx differ diff --git a/.cache/clangd/index/config.h.03B8421B881A1B3F.idx b/.cache/clangd/index/config.h.03B8421B881A1B3F.idx new file mode 100644 index 0000000..abc6139 Binary files /dev/null and b/.cache/clangd/index/config.h.03B8421B881A1B3F.idx differ diff --git a/.cache/clangd/index/context.c.EDA476931D1F952E.idx b/.cache/clangd/index/context.c.EDA476931D1F952E.idx new file mode 100644 index 0000000..bba04ee Binary files /dev/null and b/.cache/clangd/index/context.c.EDA476931D1F952E.idx differ diff --git a/.cache/clangd/index/dr_mp3.h.251364C12EEB63EE.idx b/.cache/clangd/index/dr_mp3.h.251364C12EEB63EE.idx new file mode 100644 index 0000000..9f0f41a Binary files /dev/null and b/.cache/clangd/index/dr_mp3.h.251364C12EEB63EE.idx differ diff --git a/.cache/clangd/index/dr_wav.h.57B96FF54B7B9AA9.idx b/.cache/clangd/index/dr_wav.h.57B96FF54B7B9AA9.idx new file mode 100644 index 0000000..d0b0007 Binary files /dev/null and b/.cache/clangd/index/dr_wav.h.57B96FF54B7B9AA9.idx differ diff --git a/.cache/clangd/index/egl_context.c.2879CBBF04434D06.idx b/.cache/clangd/index/egl_context.c.2879CBBF04434D06.idx new file mode 100644 index 0000000..3bcb2bb Binary files /dev/null and b/.cache/clangd/index/egl_context.c.2879CBBF04434D06.idx differ diff --git a/.cache/clangd/index/fa-solid-900.h.DCA613D0F857190A.idx b/.cache/clangd/index/fa-solid-900.h.DCA613D0F857190A.idx new file mode 100644 index 0000000..db26216 Binary files /dev/null and b/.cache/clangd/index/fa-solid-900.h.DCA613D0F857190A.idx differ diff --git a/.cache/clangd/index/glad.h.D7F215BB4A184C68.idx b/.cache/clangd/index/glad.h.D7F215BB4A184C68.idx new file mode 100644 index 0000000..819ecfd Binary files /dev/null and b/.cache/clangd/index/glad.h.D7F215BB4A184C68.idx differ diff --git a/.cache/clangd/index/glfw3.h.4A7AA4589B3A7536.idx b/.cache/clangd/index/glfw3.h.4A7AA4589B3A7536.idx new file mode 100644 index 0000000..4f86c84 Binary files /dev/null and b/.cache/clangd/index/glfw3.h.4A7AA4589B3A7536.idx differ diff --git a/.cache/clangd/index/glfw3native.h.014FFEC0ED3EC0CE.idx b/.cache/clangd/index/glfw3native.h.014FFEC0ED3EC0CE.idx new file mode 100644 index 0000000..b61a843 Binary files /dev/null and b/.cache/clangd/index/glfw3native.h.014FFEC0ED3EC0CE.idx differ diff --git a/.cache/clangd/index/glx_context.c.F619386CB1A90A48.idx b/.cache/clangd/index/glx_context.c.F619386CB1A90A48.idx new file mode 100644 index 0000000..d90d98f Binary files /dev/null and b/.cache/clangd/index/glx_context.c.F619386CB1A90A48.idx differ diff --git a/.cache/clangd/index/hot_reload.cpp.438F77C33B0F3987.idx b/.cache/clangd/index/hot_reload.cpp.438F77C33B0F3987.idx new file mode 100644 index 0000000..5092e67 Binary files /dev/null and b/.cache/clangd/index/hot_reload.cpp.438F77C33B0F3987.idx differ diff --git a/.cache/clangd/index/hot_reload.h.EBF600E71D50BA1D.idx b/.cache/clangd/index/hot_reload.h.EBF600E71D50BA1D.idx new file mode 100644 index 0000000..0de7bad Binary files /dev/null and b/.cache/clangd/index/hot_reload.h.EBF600E71D50BA1D.idx differ diff --git a/.cache/clangd/index/imconfig.h.D0F566EA74959128.idx b/.cache/clangd/index/imconfig.h.D0F566EA74959128.idx new file mode 100644 index 0000000..bcc6e19 Binary files /dev/null and b/.cache/clangd/index/imconfig.h.D0F566EA74959128.idx differ diff --git a/.cache/clangd/index/imgui.cpp.13AB39A0FB035459.idx b/.cache/clangd/index/imgui.cpp.13AB39A0FB035459.idx new file mode 100644 index 0000000..270f28e Binary files /dev/null and b/.cache/clangd/index/imgui.cpp.13AB39A0FB035459.idx differ diff --git a/.cache/clangd/index/imgui.h.B7C59F8F3DC0FD23.idx b/.cache/clangd/index/imgui.h.B7C59F8F3DC0FD23.idx new file mode 100644 index 0000000..1870af1 Binary files /dev/null and b/.cache/clangd/index/imgui.h.B7C59F8F3DC0FD23.idx differ diff --git a/.cache/clangd/index/imgui_draw.cpp.7992492567CA8F61.idx b/.cache/clangd/index/imgui_draw.cpp.7992492567CA8F61.idx new file mode 100644 index 0000000..246a7db Binary files /dev/null and b/.cache/clangd/index/imgui_draw.cpp.7992492567CA8F61.idx differ diff --git a/.cache/clangd/index/imgui_impl_raylib.h.F2883BD4DF99D0AA.idx b/.cache/clangd/index/imgui_impl_raylib.h.F2883BD4DF99D0AA.idx new file mode 100644 index 0000000..2df90c7 Binary files /dev/null and b/.cache/clangd/index/imgui_impl_raylib.h.F2883BD4DF99D0AA.idx differ diff --git a/.cache/clangd/index/imgui_internal.h.84DDA470F673C97A.idx b/.cache/clangd/index/imgui_internal.h.84DDA470F673C97A.idx new file mode 100644 index 0000000..303a220 Binary files /dev/null and b/.cache/clangd/index/imgui_internal.h.84DDA470F673C97A.idx differ diff --git a/.cache/clangd/index/imgui_tables.cpp.425F22EB42415313.idx b/.cache/clangd/index/imgui_tables.cpp.425F22EB42415313.idx new file mode 100644 index 0000000..a07ba1f Binary files /dev/null and b/.cache/clangd/index/imgui_tables.cpp.425F22EB42415313.idx differ diff --git a/.cache/clangd/index/imgui_widgets.cpp.04F11CC794B17899.idx b/.cache/clangd/index/imgui_widgets.cpp.04F11CC794B17899.idx new file mode 100644 index 0000000..a5f7e15 Binary files /dev/null and b/.cache/clangd/index/imgui_widgets.cpp.04F11CC794B17899.idx differ diff --git a/.cache/clangd/index/imstb_rectpack.h.C5897263BE31F96A.idx b/.cache/clangd/index/imstb_rectpack.h.C5897263BE31F96A.idx new file mode 100644 index 0000000..e5047d8 Binary files /dev/null and b/.cache/clangd/index/imstb_rectpack.h.C5897263BE31F96A.idx differ diff --git a/.cache/clangd/index/imstb_textedit.h.7F8427DC7CF4AEBF.idx b/.cache/clangd/index/imstb_textedit.h.7F8427DC7CF4AEBF.idx new file mode 100644 index 0000000..a498188 Binary files /dev/null and b/.cache/clangd/index/imstb_textedit.h.7F8427DC7CF4AEBF.idx differ diff --git a/.cache/clangd/index/imstb_truetype.h.3094409E29ACB5DB.idx b/.cache/clangd/index/imstb_truetype.h.3094409E29ACB5DB.idx new file mode 100644 index 0000000..34f5f6a Binary files /dev/null and b/.cache/clangd/index/imstb_truetype.h.3094409E29ACB5DB.idx differ diff --git a/.cache/clangd/index/init.c.DD5A48A85530AE07.idx b/.cache/clangd/index/init.c.DD5A48A85530AE07.idx new file mode 100644 index 0000000..3125efe Binary files /dev/null and b/.cache/clangd/index/init.c.DD5A48A85530AE07.idx differ diff --git a/.cache/clangd/index/input.c.DFC3C828DE1E8852.idx b/.cache/clangd/index/input.c.DFC3C828DE1E8852.idx new file mode 100644 index 0000000..5e16233 Binary files /dev/null and b/.cache/clangd/index/input.c.DFC3C828DE1E8852.idx differ diff --git a/.cache/clangd/index/internal.h.6CB304F22674393A.idx b/.cache/clangd/index/internal.h.6CB304F22674393A.idx new file mode 100644 index 0000000..92c9550 Binary files /dev/null and b/.cache/clangd/index/internal.h.6CB304F22674393A.idx differ diff --git a/.cache/clangd/index/jar_mod.h.CCA482DB947C5FCB.idx b/.cache/clangd/index/jar_mod.h.CCA482DB947C5FCB.idx new file mode 100644 index 0000000..ed76b04 Binary files /dev/null and b/.cache/clangd/index/jar_mod.h.CCA482DB947C5FCB.idx differ diff --git a/.cache/clangd/index/jar_xm.h.8D41367F0ED13D7A.idx b/.cache/clangd/index/jar_xm.h.8D41367F0ED13D7A.idx new file mode 100644 index 0000000..5b925bd Binary files /dev/null and b/.cache/clangd/index/jar_xm.h.8D41367F0ED13D7A.idx differ diff --git a/.cache/clangd/index/linux_joystick.c.31625443FFAB8C8B.idx b/.cache/clangd/index/linux_joystick.c.31625443FFAB8C8B.idx new file mode 100644 index 0000000..dea944e Binary files /dev/null and b/.cache/clangd/index/linux_joystick.c.31625443FFAB8C8B.idx differ diff --git a/.cache/clangd/index/linux_joystick.h.C8553F75C24E036B.idx b/.cache/clangd/index/linux_joystick.h.C8553F75C24E036B.idx new file mode 100644 index 0000000..20d3112 Binary files /dev/null and b/.cache/clangd/index/linux_joystick.h.C8553F75C24E036B.idx differ diff --git a/.cache/clangd/index/log.c.CA61F35B04D42E37.idx b/.cache/clangd/index/log.c.CA61F35B04D42E37.idx new file mode 100644 index 0000000..e3f53b5 Binary files /dev/null and b/.cache/clangd/index/log.c.CA61F35B04D42E37.idx differ diff --git a/.cache/clangd/index/log.h.032C30DFDD3DB212.idx b/.cache/clangd/index/log.h.032C30DFDD3DB212.idx new file mode 100644 index 0000000..5ddbf40 Binary files /dev/null and b/.cache/clangd/index/log.h.032C30DFDD3DB212.idx differ diff --git a/.cache/clangd/index/m3d.h.4DC2D9ED5C203922.idx b/.cache/clangd/index/m3d.h.4DC2D9ED5C203922.idx new file mode 100644 index 0000000..5fafd6c Binary files /dev/null and b/.cache/clangd/index/m3d.h.4DC2D9ED5C203922.idx differ diff --git a/.cache/clangd/index/main.cpp.C941A06453C00054.idx b/.cache/clangd/index/main.cpp.C941A06453C00054.idx new file mode 100644 index 0000000..829a2af Binary files /dev/null and b/.cache/clangd/index/main.cpp.C941A06453C00054.idx differ diff --git a/.cache/clangd/index/mappings.h.4BEBC502A0B05FF6.idx b/.cache/clangd/index/mappings.h.4BEBC502A0B05FF6.idx new file mode 100644 index 0000000..9567f84 Binary files /dev/null and b/.cache/clangd/index/mappings.h.4BEBC502A0B05FF6.idx differ diff --git a/.cache/clangd/index/math_bindings_tests.cpp.D7C83C93D7E83F9C.idx b/.cache/clangd/index/math_bindings_tests.cpp.D7C83C93D7E83F9C.idx new file mode 100644 index 0000000..d0c3807 Binary files /dev/null and b/.cache/clangd/index/math_bindings_tests.cpp.D7C83C93D7E83F9C.idx differ diff --git a/.cache/clangd/index/math_bindings_tests.h.991FEE567F64F8D3.idx b/.cache/clangd/index/math_bindings_tests.h.991FEE567F64F8D3.idx new file mode 100644 index 0000000..f131459 Binary files /dev/null and b/.cache/clangd/index/math_bindings_tests.h.991FEE567F64F8D3.idx differ diff --git a/.cache/clangd/index/miniaudio.h.0A39D8158432017C.idx b/.cache/clangd/index/miniaudio.h.0A39D8158432017C.idx new file mode 100644 index 0000000..b3b57a4 Binary files /dev/null and b/.cache/clangd/index/miniaudio.h.0A39D8158432017C.idx differ diff --git a/.cache/clangd/index/monitor.c.3469985774081EC8.idx b/.cache/clangd/index/monitor.c.3469985774081EC8.idx new file mode 100644 index 0000000..e85c491 Binary files /dev/null and b/.cache/clangd/index/monitor.c.3469985774081EC8.idx differ diff --git a/.cache/clangd/index/msf_gif.h.65FFB47C06B3BA6A.idx b/.cache/clangd/index/msf_gif.h.65FFB47C06B3BA6A.idx new file mode 100644 index 0000000..86bf69f Binary files /dev/null and b/.cache/clangd/index/msf_gif.h.65FFB47C06B3BA6A.idx differ diff --git a/.cache/clangd/index/null_init.c.E38F9C95980E7054.idx b/.cache/clangd/index/null_init.c.E38F9C95980E7054.idx new file mode 100644 index 0000000..eaacd20 Binary files /dev/null and b/.cache/clangd/index/null_init.c.E38F9C95980E7054.idx differ diff --git a/.cache/clangd/index/null_joystick.c.3827883193D34A5A.idx b/.cache/clangd/index/null_joystick.c.3827883193D34A5A.idx new file mode 100644 index 0000000..bb784a3 Binary files /dev/null and b/.cache/clangd/index/null_joystick.c.3827883193D34A5A.idx differ diff --git a/.cache/clangd/index/null_joystick.h.7ADCE9E49835732C.idx b/.cache/clangd/index/null_joystick.h.7ADCE9E49835732C.idx new file mode 100644 index 0000000..fdf043e Binary files /dev/null and b/.cache/clangd/index/null_joystick.h.7ADCE9E49835732C.idx differ diff --git a/.cache/clangd/index/null_monitor.c.34BD3DBD2B22C4F5.idx b/.cache/clangd/index/null_monitor.c.34BD3DBD2B22C4F5.idx new file mode 100644 index 0000000..e51e0e9 Binary files /dev/null and b/.cache/clangd/index/null_monitor.c.34BD3DBD2B22C4F5.idx differ diff --git a/.cache/clangd/index/null_platform.h.7A0422E28E0FB8E5.idx b/.cache/clangd/index/null_platform.h.7A0422E28E0FB8E5.idx new file mode 100644 index 0000000..20ffdbe Binary files /dev/null and b/.cache/clangd/index/null_platform.h.7A0422E28E0FB8E5.idx differ diff --git a/.cache/clangd/index/null_window.c.FB322F091A96B762.idx b/.cache/clangd/index/null_window.c.FB322F091A96B762.idx new file mode 100644 index 0000000..9eb6b63 Binary files /dev/null and b/.cache/clangd/index/null_window.c.FB322F091A96B762.idx differ diff --git a/.cache/clangd/index/osmesa_context.c.2BA66DA2FB27F750.idx b/.cache/clangd/index/osmesa_context.c.2BA66DA2FB27F750.idx new file mode 100644 index 0000000..7567af6 Binary files /dev/null and b/.cache/clangd/index/osmesa_context.c.2BA66DA2FB27F750.idx differ diff --git a/.cache/clangd/index/par_shapes.h.7393F928DA3A6201.idx b/.cache/clangd/index/par_shapes.h.7393F928DA3A6201.idx new file mode 100644 index 0000000..992d443 Binary files /dev/null and b/.cache/clangd/index/par_shapes.h.7393F928DA3A6201.idx differ diff --git a/.cache/clangd/index/platform.c.FA48B8A9BC845731.idx b/.cache/clangd/index/platform.c.FA48B8A9BC845731.idx new file mode 100644 index 0000000..4dc4156 Binary files /dev/null and b/.cache/clangd/index/platform.c.FA48B8A9BC845731.idx differ diff --git a/.cache/clangd/index/platform.h.BF727383B031E461.idx b/.cache/clangd/index/platform.h.BF727383B031E461.idx new file mode 100644 index 0000000..a199f1a Binary files /dev/null and b/.cache/clangd/index/platform.h.BF727383B031E461.idx differ diff --git a/.cache/clangd/index/posix_module.c.CE3721E5E9F44037.idx b/.cache/clangd/index/posix_module.c.CE3721E5E9F44037.idx new file mode 100644 index 0000000..c897462 Binary files /dev/null and b/.cache/clangd/index/posix_module.c.CE3721E5E9F44037.idx differ diff --git a/.cache/clangd/index/posix_poll.c.63C3DBF0634C78B9.idx b/.cache/clangd/index/posix_poll.c.63C3DBF0634C78B9.idx new file mode 100644 index 0000000..0f619c0 Binary files /dev/null and b/.cache/clangd/index/posix_poll.c.63C3DBF0634C78B9.idx differ diff --git a/.cache/clangd/index/posix_poll.h.761D8493A7E79417.idx b/.cache/clangd/index/posix_poll.h.761D8493A7E79417.idx new file mode 100644 index 0000000..6342afb Binary files /dev/null and b/.cache/clangd/index/posix_poll.h.761D8493A7E79417.idx differ diff --git a/.cache/clangd/index/posix_thread.c.8FB200A08AA28F36.idx b/.cache/clangd/index/posix_thread.c.8FB200A08AA28F36.idx new file mode 100644 index 0000000..e1ad9e7 Binary files /dev/null and b/.cache/clangd/index/posix_thread.c.8FB200A08AA28F36.idx differ diff --git a/.cache/clangd/index/posix_thread.h.C5A22A3F1C04EBDA.idx b/.cache/clangd/index/posix_thread.h.C5A22A3F1C04EBDA.idx new file mode 100644 index 0000000..a127e5d Binary files /dev/null and b/.cache/clangd/index/posix_thread.h.C5A22A3F1C04EBDA.idx differ diff --git a/.cache/clangd/index/posix_time.c.EB737AF6C0A11311.idx b/.cache/clangd/index/posix_time.c.EB737AF6C0A11311.idx new file mode 100644 index 0000000..50bc172 Binary files /dev/null and b/.cache/clangd/index/posix_time.c.EB737AF6C0A11311.idx differ diff --git a/.cache/clangd/index/posix_time.h.1D027F288CF45AAB.idx b/.cache/clangd/index/posix_time.h.1D027F288CF45AAB.idx new file mode 100644 index 0000000..fcb1102 Binary files /dev/null and b/.cache/clangd/index/posix_time.h.1D027F288CF45AAB.idx differ diff --git a/.cache/clangd/index/qoa.h.8814EBDB08EED1F1.idx b/.cache/clangd/index/qoa.h.8814EBDB08EED1F1.idx new file mode 100644 index 0000000..2f7a430 Binary files /dev/null and b/.cache/clangd/index/qoa.h.8814EBDB08EED1F1.idx differ diff --git a/.cache/clangd/index/qoaplay.c.88D0F007DC759AB0.idx b/.cache/clangd/index/qoaplay.c.88D0F007DC759AB0.idx new file mode 100644 index 0000000..1ce2375 Binary files /dev/null and b/.cache/clangd/index/qoaplay.c.88D0F007DC759AB0.idx differ diff --git a/.cache/clangd/index/qoi.h.AADB9CA1D27BDEE2.idx b/.cache/clangd/index/qoi.h.AADB9CA1D27BDEE2.idx new file mode 100644 index 0000000..bdb8ff5 Binary files /dev/null and b/.cache/clangd/index/qoi.h.AADB9CA1D27BDEE2.idx differ diff --git a/.cache/clangd/index/raudio.c.67BD55980473245D.idx b/.cache/clangd/index/raudio.c.67BD55980473245D.idx new file mode 100644 index 0000000..2555c07 Binary files /dev/null and b/.cache/clangd/index/raudio.c.67BD55980473245D.idx differ diff --git a/.cache/clangd/index/raylib.h.29612B7258BE815F.idx b/.cache/clangd/index/raylib.h.29612B7258BE815F.idx new file mode 100644 index 0000000..1d70127 Binary files /dev/null and b/.cache/clangd/index/raylib.h.29612B7258BE815F.idx differ diff --git a/.cache/clangd/index/raymath.h.2370950AA6487104.idx b/.cache/clangd/index/raymath.h.2370950AA6487104.idx new file mode 100644 index 0000000..e8d81c6 Binary files /dev/null and b/.cache/clangd/index/raymath.h.2370950AA6487104.idx differ diff --git a/.cache/clangd/index/rcamera.h.C158CEF4110F1CE8.idx b/.cache/clangd/index/rcamera.h.C158CEF4110F1CE8.idx new file mode 100644 index 0000000..1501e5a Binary files /dev/null and b/.cache/clangd/index/rcamera.h.C158CEF4110F1CE8.idx differ diff --git a/.cache/clangd/index/rcore.c.636D60F9ED989DDC.idx b/.cache/clangd/index/rcore.c.636D60F9ED989DDC.idx new file mode 100644 index 0000000..ed50b70 Binary files /dev/null and b/.cache/clangd/index/rcore.c.636D60F9ED989DDC.idx differ diff --git a/.cache/clangd/index/rcore_desktop_glfw.c.D1400F1522C61D29.idx b/.cache/clangd/index/rcore_desktop_glfw.c.D1400F1522C61D29.idx new file mode 100644 index 0000000..3c53781 Binary files /dev/null and b/.cache/clangd/index/rcore_desktop_glfw.c.D1400F1522C61D29.idx differ diff --git a/.cache/clangd/index/rgestures.h.1ED1EE8399665915.idx b/.cache/clangd/index/rgestures.h.1ED1EE8399665915.idx new file mode 100644 index 0000000..b05df09 Binary files /dev/null and b/.cache/clangd/index/rgestures.h.1ED1EE8399665915.idx differ diff --git a/.cache/clangd/index/rlImGui.cpp.A1A91F5C2EC33896.idx b/.cache/clangd/index/rlImGui.cpp.A1A91F5C2EC33896.idx new file mode 100644 index 0000000..c3a35f6 Binary files /dev/null and b/.cache/clangd/index/rlImGui.cpp.A1A91F5C2EC33896.idx differ diff --git a/.cache/clangd/index/rlImGui.h.38EE9B31246D52A1.idx b/.cache/clangd/index/rlImGui.h.38EE9B31246D52A1.idx new file mode 100644 index 0000000..5618253 Binary files /dev/null and b/.cache/clangd/index/rlImGui.h.38EE9B31246D52A1.idx differ diff --git a/.cache/clangd/index/rl_gputex.h.5F186F0D984189E0.idx b/.cache/clangd/index/rl_gputex.h.5F186F0D984189E0.idx new file mode 100644 index 0000000..2b83b65 Binary files /dev/null and b/.cache/clangd/index/rl_gputex.h.5F186F0D984189E0.idx differ diff --git a/.cache/clangd/index/rlgl.h.EE8C9BD4F86A9217.idx b/.cache/clangd/index/rlgl.h.EE8C9BD4F86A9217.idx new file mode 100644 index 0000000..6269794 Binary files /dev/null and b/.cache/clangd/index/rlgl.h.EE8C9BD4F86A9217.idx differ diff --git a/.cache/clangd/index/rmodels.c.E2077C168069643D.idx b/.cache/clangd/index/rmodels.c.E2077C168069643D.idx new file mode 100644 index 0000000..93d22eb Binary files /dev/null and b/.cache/clangd/index/rmodels.c.E2077C168069643D.idx differ diff --git a/.cache/clangd/index/rprand.h.A9F7B76391CEC0D4.idx b/.cache/clangd/index/rprand.h.A9F7B76391CEC0D4.idx new file mode 100644 index 0000000..a3cdbcf Binary files /dev/null and b/.cache/clangd/index/rprand.h.A9F7B76391CEC0D4.idx differ diff --git a/.cache/clangd/index/rshapes.c.F9C87CED8A5C502A.idx b/.cache/clangd/index/rshapes.c.F9C87CED8A5C502A.idx new file mode 100644 index 0000000..4529a5a Binary files /dev/null and b/.cache/clangd/index/rshapes.c.F9C87CED8A5C502A.idx differ diff --git a/.cache/clangd/index/rtext.c.6F870283BFAE90A6.idx b/.cache/clangd/index/rtext.c.6F870283BFAE90A6.idx new file mode 100644 index 0000000..0641571 Binary files /dev/null and b/.cache/clangd/index/rtext.c.6F870283BFAE90A6.idx differ diff --git a/.cache/clangd/index/rtextures.c.A0FBFE53B2EC34A3.idx b/.cache/clangd/index/rtextures.c.A0FBFE53B2EC34A3.idx new file mode 100644 index 0000000..73da652 Binary files /dev/null and b/.cache/clangd/index/rtextures.c.A0FBFE53B2EC34A3.idx differ diff --git a/.cache/clangd/index/script_engine_tests.cpp.FA7D29E2927285AC.idx b/.cache/clangd/index/script_engine_tests.cpp.FA7D29E2927285AC.idx new file mode 100644 index 0000000..d60e8b3 Binary files /dev/null and b/.cache/clangd/index/script_engine_tests.cpp.FA7D29E2927285AC.idx differ diff --git a/.cache/clangd/index/script_engine_tests.h.3AFD5906B4BAFB23.idx b/.cache/clangd/index/script_engine_tests.h.3AFD5906B4BAFB23.idx new file mode 100644 index 0000000..0a10629 Binary files /dev/null and b/.cache/clangd/index/script_engine_tests.h.3AFD5906B4BAFB23.idx differ diff --git a/.cache/clangd/index/scriptbuilder.cpp.4174B604021EDCA8.idx b/.cache/clangd/index/scriptbuilder.cpp.4174B604021EDCA8.idx new file mode 100644 index 0000000..9862c86 Binary files /dev/null and b/.cache/clangd/index/scriptbuilder.cpp.4174B604021EDCA8.idx differ diff --git a/.cache/clangd/index/scriptbuilder.h.01A4572D6A536522.idx b/.cache/clangd/index/scriptbuilder.h.01A4572D6A536522.idx new file mode 100644 index 0000000..0cb04bc Binary files /dev/null and b/.cache/clangd/index/scriptbuilder.h.01A4572D6A536522.idx differ diff --git a/.cache/clangd/index/scriptstdstring.cpp.C8D72FED91DBFBC6.idx b/.cache/clangd/index/scriptstdstring.cpp.C8D72FED91DBFBC6.idx new file mode 100644 index 0000000..b5f5287 Binary files /dev/null and b/.cache/clangd/index/scriptstdstring.cpp.C8D72FED91DBFBC6.idx differ diff --git a/.cache/clangd/index/scriptstdstring.h.0BCD3B6B171D685C.idx b/.cache/clangd/index/scriptstdstring.h.0BCD3B6B171D685C.idx new file mode 100644 index 0000000..7df1654 Binary files /dev/null and b/.cache/clangd/index/scriptstdstring.h.0BCD3B6B171D685C.idx differ diff --git a/.cache/clangd/index/sdefl.h.C0C0E9AFD8034531.idx b/.cache/clangd/index/sdefl.h.C0C0E9AFD8034531.idx new file mode 100644 index 0000000..06a9053 Binary files /dev/null and b/.cache/clangd/index/sdefl.h.C0C0E9AFD8034531.idx differ diff --git a/.cache/clangd/index/sinfl.h.62097C66CA8991F5.idx b/.cache/clangd/index/sinfl.h.62097C66CA8991F5.idx new file mode 100644 index 0000000..196bf31 Binary files /dev/null and b/.cache/clangd/index/sinfl.h.62097C66CA8991F5.idx differ diff --git a/.cache/clangd/index/stb_image.h.181F33D2B7AF82DE.idx b/.cache/clangd/index/stb_image.h.181F33D2B7AF82DE.idx new file mode 100644 index 0000000..9245188 Binary files /dev/null and b/.cache/clangd/index/stb_image.h.181F33D2B7AF82DE.idx differ diff --git a/.cache/clangd/index/stb_image_resize2.h.CD8FAB1902B73085.idx b/.cache/clangd/index/stb_image_resize2.h.CD8FAB1902B73085.idx new file mode 100644 index 0000000..62bb7e2 Binary files /dev/null and b/.cache/clangd/index/stb_image_resize2.h.CD8FAB1902B73085.idx differ diff --git a/.cache/clangd/index/stb_image_write.h.E67C86A994AE4948.idx b/.cache/clangd/index/stb_image_write.h.E67C86A994AE4948.idx new file mode 100644 index 0000000..c24e6d1 Binary files /dev/null and b/.cache/clangd/index/stb_image_write.h.E67C86A994AE4948.idx differ diff --git a/.cache/clangd/index/stb_perlin.h.00622C51F350CC49.idx b/.cache/clangd/index/stb_perlin.h.00622C51F350CC49.idx new file mode 100644 index 0000000..cde4ce7 Binary files /dev/null and b/.cache/clangd/index/stb_perlin.h.00622C51F350CC49.idx differ diff --git a/.cache/clangd/index/stb_rect_pack.h.D251B0730CD219ED.idx b/.cache/clangd/index/stb_rect_pack.h.D251B0730CD219ED.idx new file mode 100644 index 0000000..4637861 Binary files /dev/null and b/.cache/clangd/index/stb_rect_pack.h.D251B0730CD219ED.idx differ diff --git a/.cache/clangd/index/stb_truetype.h.07A6482B66454C43.idx b/.cache/clangd/index/stb_truetype.h.07A6482B66454C43.idx new file mode 100644 index 0000000..e689904 Binary files /dev/null and b/.cache/clangd/index/stb_truetype.h.07A6482B66454C43.idx differ diff --git a/.cache/clangd/index/stb_vorbis.c.3F18A71DA2EB018C.idx b/.cache/clangd/index/stb_vorbis.c.3F18A71DA2EB018C.idx new file mode 100644 index 0000000..0fd0df8 Binary files /dev/null and b/.cache/clangd/index/stb_vorbis.c.3F18A71DA2EB018C.idx differ diff --git a/.cache/clangd/index/test.cpp.9DF026EB2D4E3520.idx b/.cache/clangd/index/test.cpp.9DF026EB2D4E3520.idx new file mode 100644 index 0000000..6daec03 Binary files /dev/null and b/.cache/clangd/index/test.cpp.9DF026EB2D4E3520.idx differ diff --git a/.cache/clangd/index/tinyobj_loader_c.h.CCE1E6C571F96984.idx b/.cache/clangd/index/tinyobj_loader_c.h.CCE1E6C571F96984.idx new file mode 100644 index 0000000..263684b Binary files /dev/null and b/.cache/clangd/index/tinyobj_loader_c.h.CCE1E6C571F96984.idx differ diff --git a/.cache/clangd/index/utils.c.9E0AC1AFB528EC97.idx b/.cache/clangd/index/utils.c.9E0AC1AFB528EC97.idx new file mode 100644 index 0000000..a5c9c2c Binary files /dev/null and b/.cache/clangd/index/utils.c.9E0AC1AFB528EC97.idx differ diff --git a/.cache/clangd/index/utils.h.B2A9FDFBB6793C09.idx b/.cache/clangd/index/utils.h.B2A9FDFBB6793C09.idx new file mode 100644 index 0000000..486205c Binary files /dev/null and b/.cache/clangd/index/utils.h.B2A9FDFBB6793C09.idx differ diff --git a/.cache/clangd/index/vox_loader.h.9A8296C333F5F41B.idx b/.cache/clangd/index/vox_loader.h.9A8296C333F5F41B.idx new file mode 100644 index 0000000..0e60c48 Binary files /dev/null and b/.cache/clangd/index/vox_loader.h.9A8296C333F5F41B.idx differ diff --git a/.cache/clangd/index/vulkan.c.EF7A63E0933C6FE4.idx b/.cache/clangd/index/vulkan.c.EF7A63E0933C6FE4.idx new file mode 100644 index 0000000..217a2a7 Binary files /dev/null and b/.cache/clangd/index/vulkan.c.EF7A63E0933C6FE4.idx differ diff --git a/.cache/clangd/index/window.c.0CB305403CB68121.idx b/.cache/clangd/index/window.c.0CB305403CB68121.idx new file mode 100644 index 0000000..4a44542 Binary files /dev/null and b/.cache/clangd/index/window.c.0CB305403CB68121.idx differ diff --git a/.cache/clangd/index/x11_init.c.87FCA90C32F5E445.idx b/.cache/clangd/index/x11_init.c.87FCA90C32F5E445.idx new file mode 100644 index 0000000..a793cdf Binary files /dev/null and b/.cache/clangd/index/x11_init.c.87FCA90C32F5E445.idx differ diff --git a/.cache/clangd/index/x11_monitor.c.4A77A94C2FF11C1D.idx b/.cache/clangd/index/x11_monitor.c.4A77A94C2FF11C1D.idx new file mode 100644 index 0000000..6334e1c Binary files /dev/null and b/.cache/clangd/index/x11_monitor.c.4A77A94C2FF11C1D.idx differ diff --git a/.cache/clangd/index/x11_platform.h.91FEF7ED090E8205.idx b/.cache/clangd/index/x11_platform.h.91FEF7ED090E8205.idx new file mode 100644 index 0000000..cd805f1 Binary files /dev/null and b/.cache/clangd/index/x11_platform.h.91FEF7ED090E8205.idx differ diff --git a/.cache/clangd/index/x11_window.c.DCD2EDB15A9C6D96.idx b/.cache/clangd/index/x11_window.c.DCD2EDB15A9C6D96.idx new file mode 100644 index 0000000..018a761 Binary files /dev/null and b/.cache/clangd/index/x11_window.c.DCD2EDB15A9C6D96.idx differ diff --git a/.cache/clangd/index/xkb_unicode.c.D2CF3715482DBC0B.idx b/.cache/clangd/index/xkb_unicode.c.D2CF3715482DBC0B.idx new file mode 100644 index 0000000..aea8bbc Binary files /dev/null and b/.cache/clangd/index/xkb_unicode.c.D2CF3715482DBC0B.idx differ diff --git a/.cache/clangd/index/xkb_unicode.h.73350CFC11A68654.idx b/.cache/clangd/index/xkb_unicode.h.73350CFC11A68654.idx new file mode 100644 index 0000000..86810c2 Binary files /dev/null and b/.cache/clangd/index/xkb_unicode.h.73350CFC11A68654.idx differ diff --git a/.gitea/workflows/sync-docs-to-wiki.yml b/.gitea/workflows/sync-docs-to-wiki.yml new file mode 100644 index 0000000..157ed62 --- /dev/null +++ b/.gitea/workflows/sync-docs-to-wiki.yml @@ -0,0 +1,54 @@ +name: Sync Docs to Gitea Wiki + +on: + push: + paths: + - 'docs/**' + +jobs: + sync: + name: Sync docs to Gitea wiki + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Start SSH agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.WIKI_SSH_KEY }} + + - name: Add Gitea host key + run: | + mkdir -p ~/.ssh + ssh-keyscan -t rsa gitea.appstack.me >> ~/.ssh/known_hosts + + - name: Sync docs to wiki + env: + WIKI_REPO: git@gitea.appstack.me:nick/simian.wiki.git + run: | + git config --global user.name "Simian CI" + git config --global user.email "ci@simian.local" + + # Workspace is always $GITHUB_WORKSPACE in Actions + WORKSPACE=${GITHUB_WORKSPACE:-.} + + TEMP_WIKI=$(mktemp -d) + git clone "$WIKI_REPO" "$TEMP_WIKI" + + cd "$TEMP_WIKI" + + # Safe clean (keep .git) + find . -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} + + + # Copy from workspace/docs + cp -r "$WORKSPACE/docs"/* . 2>/dev/null || true + + git add -A + if git diff --staged --quiet; then + echo "No wiki changes" + else + git commit -m "Update wiki from docs (CI) [skip ci]" + git push origin main + echo "✅ Synced to simian.wiki main" + fi diff --git a/.gitignore b/.gitignore index 27ba0d8..6c3fdb2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,45 @@ -build/ -log.txt \ No newline at end of file +/build/ +/log.txt + +# CMake build folders +/build/ +*/CMakeFiles/ +*/CMakeCache.txt +*/Makefile +*/cmake_install.cmake +*/CPackConfig.cmake +*/CPackSourceConfig.cmake +*/CTestTestfile.cmake + +# Root-level CMake artifacts +/Makefile +/cmake_install.cmake +/CPackConfig.cmake +/CPackSourceConfig.cmake +/CTestTestfile.cmake +/cmake/ + +# Intermediate objects, libraries, executables +*.o +*.obj +*.lo +*.a +*.la +*.so +*.dylib +*.dll +*.exe +*.out +*.pdb +*.mod +*.dir/ + +# Optional: extra CMake junk in source tree +CMakeScripts/ +CMakeFiles/ +CMakeCache.txt +Testing/ + +# Editor state files +.ned-agent-history.json +.undo-redo-ned.json \ No newline at end of file diff --git a/.gitmodules b/.gitmodules index cd4f90d..482df24 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,12 +4,12 @@ [submodule "external/angelscript"] path = external/angelscript url = https://github.com/anjo76/angelscript.git -[submodule "external/acutest"] - path = external/acutest - url = https://github.com/mity/acutest [submodule "external/imgui"] path = external/imgui url = https://github.com/ocornut/imgui.git [submodule "external/rlImGui"] path = external/rlImGui url = https://github.com/raylib-extras/rlImGui.git +[submodule "external/physfs"] + path = external/physfs + url = https://github.com/icculus/physfs diff --git a/CMakeLists.txt b/CMakeLists.txt index 77b91d5..3102dca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,11 @@ add_subdirectory(external/raylib) # ------------------------- add_subdirectory(external/angelscript/sdk/angelscript/projects/cmake) +# ------------------------- +# PhysFS (submodule) +# ------------------------- +add_subdirectory(external/physfs) + # ------------------------- # Dear ImGui (docking branch) # ------------------------- @@ -42,11 +47,6 @@ else() set(HAVE_RLIMGUI FALSE) endif() # ------------------------- -# Tests -# ------------------------- -enable_testing() -add_subdirectory(tests) -# ------------------------- # 3️⃣ scriptstdstring add-on # ------------------------- add_library(scriptstdstring STATIC @@ -69,49 +69,93 @@ target_include_directories(scriptbuilder ) # ------------------------- -# 4️⃣ Main executable +# 3️⃣.2️⃣ scriptarray add-on # ------------------------- -add_executable(simian - main.cpp - src/Application.cpp - src/ScriptEngine.cpp - src/ScriptBindings.cpp - src/HotReload.cpp - src/GuiManager.cpp - src/LogWindow.cpp - src/log/log.c +add_library(scriptarray STATIC + external/angelscript/sdk/add_on/scriptarray/scriptarray.cpp ) -target_include_directories(simian PUBLIC +target_include_directories(scriptarray + PUBLIC external/angelscript/sdk/angelscript/include +) + +# ------------------------- +# 4️⃣ Simian Core Library (SHARED CODE) +# ------------------------- +add_library(simian_core STATIC + src/Application.cpp + src/PhysFSManager.cpp + src/SceneGraph.cpp + src/SceneLoader.cpp + src/scripting/ScriptEngine.cpp + src/scripting/ScriptBindings.cpp + src/scripting/ToastBindings.cpp + src/scripting/LogBindings.cpp + src/scripting/ImageBindings.cpp + src/scripting/TextureBindings.cpp + src/scripting/MathBindings.cpp + src/scripting/ECSBindings.cpp + src/scripting/ResourceBindings.cpp + src/scripting/InputBindings.cpp + src/scripting/SceneBindings.cpp + src/scripting/PhysFSBindings.cpp + src/HotReload.cpp + src/gui/GuiManager.cpp + src/gui/ImViewGuizmoIntegration.cpp + src/gui/LogWindow.cpp + src/log/log.c + src/ShaderManager.cpp + src/ModelManager.cpp + src/MaterialManager.cpp + src/AssetManager.cpp + src/TextureManager.cpp + src/InputManager.cpp +) + +target_include_directories(simian_core PUBLIC include + include/scripting + include/gui src/log external/raylib/src external/angelscript/sdk/angelscript/include external/angelscript/sdk/add_on/scriptstdstring external/angelscript/sdk/add_on/scriptbuilder - external/imgui + external/angelscript/sdk/add_on/scriptarray + external/physfs/src + external/imgui + external/ImViewGuizmo ) -target_link_libraries(simian PRIVATE +target_link_libraries(simian_core PUBLIC raylib angelscript + PhysFS::PhysFS-static scriptstdstring scriptbuilder + scriptarray imgui ) if(HAVE_RLIMGUI) - target_include_directories(simian PUBLIC ${RLIMGUI_ROOT}/include) - target_link_libraries(simian PRIVATE rlImGui) + target_include_directories(simian_core PUBLIC ${RLIMGUI_ROOT}/include) + target_link_libraries(simian_core PUBLIC rlImGui) # Expose HAVE_RLIMGUI to the C++ compiler so source can guard ImGui calls - target_compile_definitions(simian PRIVATE HAVE_RLIMGUI=1) - message(STATUS "Linking rlImGui into simian") + target_compile_definitions(simian_core PUBLIC HAVE_RLIMGUI=1) + message(STATUS "Linking rlImGui into simian_core") else() message(STATUS "rlImGui not found - building without rlImGui integration") endif() # ------------------------- -# 5️⃣ Platform-specific linking +# 5️⃣ Main executable (just main.cpp) +# ------------------------- +add_executable(simian main.cpp) + +target_link_libraries(simian PRIVATE simian_core) + +# ------------------------- +# 6️⃣ Platform-specific linking # ------------------------- if(WIN32) target_link_libraries(simian PRIVATE winmm gdi32) @@ -120,11 +164,19 @@ elseif(UNIX) endif() # ------------------------- -# 6️⃣ Optional: Compile flags +# 7️⃣ Optional: Compile flags # ------------------------- if(MSVC) - target_compile_options(simian PRIVATE /W4 /EHsc) + target_compile_options(simian_core PRIVATE /W4 /EHsc) else() - target_compile_options(simian PRIVATE -Wall -Wextra -pedantic) + target_compile_options(simian_core PRIVATE -Wall -Wextra -pedantic) + if(TARGET raylib) + target_compile_options(raylib PRIVATE -Wno-missing-field-initializers) + endif() endif() -# ------------------------- \ No newline at end of file + +# ------------------------- +# 8️⃣ Tests +# ------------------------- +enable_testing() +add_subdirectory(tests) \ No newline at end of file diff --git a/Justfile b/Justfile index a7c211f..d1574ba 100644 --- a/Justfile +++ b/Justfile @@ -40,6 +40,10 @@ run: @echo "Running simian (Release)..." {{BUILD_DIR}}\Release\simian.exe +run-editor: + @echo "Running simian (Release) with Editor..." + {{BUILD_DIR}}\Release\simian.exe --editor + # Run Debug build run-debug: @echo "Running simian (Debug)..." diff --git a/README.md b/README.md index a36580a..22e8012 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,16 @@ Simian/ - If compilation succeeds, the temp module replaces the live `main` module and new functions are used. If compilation fails, the running module is left intact and a single-line error is shown on-screen. - This avoids crashes that happen when a script with compilation errors would otherwise replace/break function pointers while the engine is executing. +## Asset registry + +Asset keys are persisted in a project-wide registry at `assets/registry.toml`. This file is loaded on startup and written on shutdown so scene references stay valid across sessions. + +## Scene scripts (class-based) + +Scene scripts now use class instances (similar to entity scripts). Implement a class with `Init()`, `Update(float)`, and `Shutdown()` methods. The engine picks the first class that exposes any of those methods and instantiates it. + +See `scripts/game.as` and `scripts/scene_example.as` for examples. + ## ImGui / rlImGui (optional) The repository prefers to keep upstream code as submodules. Some upstream projects (like certain ImGui tags or rlImGui) don't provide a CMakeLists.txt. To support those without editing submodules, the project includes small wrappers in `cmake/`: @@ -128,3 +138,4 @@ cd ../.. && git add external/raylib external/angelscript && git commit -m "Updat * Raylib: Zlib License (see https://github.com/raysan5/raylib#license) * AngelScript: MIT License (see https://github.com/angelcode/angelscript/blob/master/license.txt) * Simian: Not open source (yet) +* Entt: MIT License (see https://github.com/skypjack/entt/blob/main/LICENSE) diff --git a/assets/raylib_96x96.png b/assets/raylib_96x96.png new file mode 100644 index 0000000..1d897db Binary files /dev/null and b/assets/raylib_96x96.png differ diff --git a/assets/registry.toml b/assets/registry.toml new file mode 100644 index 0000000..0d52586 --- /dev/null +++ b/assets/registry.toml @@ -0,0 +1,15 @@ +# Simian asset registry +# Auto-generated - edits may be overwritten + +[[model]] +key = "Torus" +type = "primitive" +primitive = "torus" +params = [0.5, 1, 16, 8] + +[[model]] +key = "demo_cube" +type = "primitive" +primitive = "cube" +params = [1, 1, 1] + diff --git a/docs/Editor.md b/docs/Editor.md new file mode 100644 index 0000000..6ddff2e --- /dev/null +++ b/docs/Editor.md @@ -0,0 +1,104 @@ +# Editor Overview + +This document outlines the in-app editor UI, its panels, and common workflows. + +## Launching the Editor + +- Start the app with the editor flag enabled. +- The editor UI appears as dockable panels around the Game Window. + +## Layout + +- DockSpace host with a top menu bar. +- You can drag and drop to move panels around. +- Panels: Game Window, Scene, Assets, Inspector, Log Viewer. + +## Menu Bar + +### Game + +- Quit: request a clean shutdown. + +### Scene + +- New: clear the current scene. +- Load: load a scene from a TOML-like file path. +- Save As: save the current scene to a file path. + +### View + +- Toggle Log Viewer, Scene, and Inspector panels. +- Toggle Assets panel. + +## Game Window + +- Renders the current scene view. +- Resizes with the available docked panel space. + +## Scene Panel + +- Displays the entity hierarchy as a tree. +- Drag and drop entities to re-parent. +- Drop on the bottom target to clear parent. +- Create Entity and Delete Selected controls. + +## Inspector Panel + +- Shows components for the selected entity. +- Add Component dropdown lists only components not yet on the entity. +- Remove buttons appear on removable components. + +### Components + +- Tag: rename the entity. +- Transform: position, scale, rotation (degrees in UI). +- Velocity: linear and angular velocity. +- Model Renderer: model id, shader id, color, outline. +- Texture: texture id. +- UV Transform: scale, offset, rotation (radians). +- Script: per-entity script path and enabled toggle. +- Camera: position, target, up, FOV, projection, helper actions. +- Light: direction, color, intensity, helper presets. +- Material: material id. +- Render Pass: order, depth flags, shader id. +- World Transform: read-only computed transform. + +## Assets Panel + +- Browse loaded textures, shaders, models, and materials. +- Load assets by key and path, or create primitive models. +- Unload assets by key. + +## Log Viewer + +- Shows engine log output in real time. + +## Shortcuts + +- Ctrl+Q: Quit +- Ctrl+L: Toggle Log Viewer +- Ctrl+N: New Scene +- Ctrl+O: Load Scene +- Ctrl+S: Save Scene + +## Notes + +- Scene Save and Load use file path text fields. +- Components can be assigned via asset keys in the inspector; numeric IDs remain as a fallback. +- World Transform is computed from hierarchy and local transforms and is read-only. +- UV Transform values are passed as shader uniforms `uvScale`, `uvOffset`, and `uvRotation` when present. + +## Scene Scripts + +- Scene scripts are loaded by the scene system and run once per scene. +- The editor lets you set a scene-level script path in the Scene panel. +- Preferred scene callbacks: `SceneInit()`, `SceneUpdate(float dt)`, `SceneShutdown()`. +- Legacy callbacks still work: `Init()`, `Update(float dt)`, `Shutdown()`. + +## ScriptComponent + +- ScriptComponent runs a per-entity script module. +- Set the script path in the Inspector and toggle enabled. +- Preferred callbacks: `EntityInit(uint entity)`, `EntityUpdate(uint entity, float dt)`, `EntityShutdown(uint entity)`. +- Legacy callbacks still work: `Init(uint entity)`, `Update(uint entity, float dt)`, `Shutdown(uint entity)`. +- Subject to change diff --git a/docs/Home.md b/docs/Home.md new file mode 100644 index 0000000..04428eb --- /dev/null +++ b/docs/Home.md @@ -0,0 +1,11 @@ +# Simian Documentation + +This folder contains project documentation that is synced to the project's Gitea wiki. +Edit markdown files here and push to the repository; CI will mirror them to the wiki. + +## Pages +- [Scripting](Scripting.md) — scripting API and examples +- [Usage](Usage.md) — getting started and build instructions +- [Scenegraph](Scenegraph.md) — hierarchy components and scene format +- [Editor](Editor.md) - details on how to use the editor + diff --git a/docs/Scenegraph.md b/docs/Scenegraph.md new file mode 100644 index 0000000..62c5a5a --- /dev/null +++ b/docs/Scenegraph.md @@ -0,0 +1,124 @@ +# Scenegraph + +This project uses a lightweight hierarchy component to model parent/child relationships in ECS. +The scenegraph is EnTT-friendly and uses a linked-list structure for children. + +## Components + +### `Hierarchy` +Each entity can have a `Hierarchy` component with these fields: + +- `parent` — parent entity or `null` +- `first` — first child entity +- `prev` / `next` — sibling links + +This mirrors the common EnTT scenegraph pattern and avoids storing vectors of children inside a component. + +### `ECSTransform` (local) +Stores local position/scale and a Y-axis rotation (radians). + +### `WorldTransform` (computed) +Computed each frame from the hierarchy and local transform. Rendering uses `WorldTransform` so children move with parents. + +## Update Order + +1. Systems update local transforms (e.g., velocity system). +2. `UpdateWorldTransforms()` traverses the hierarchy and computes `WorldTransform`. +3. Rendering draws from `WorldTransform` + `ModelRenderer`. + +## Script API + +```angelscript +ECS::SetParent(child, parent); +ECS::ClearParent(child); +uint p = ECS::GetParent(child); +bool has = ECS::HasParent(child); +``` + +## Scene File Format (TOML) + +Scenes are stored in `scenes/*.toml` using a minimal TOML-like structure. Example: + +```toml +scene_script = "scripts/scene.as" + +[[entity]] +id = "root" +tag = "Root" +position = [0.0, 0.0, 0.0] +rotation_deg = 0.0 +scale = [1.0, 1.0, 1.0] + +[[entity]] +id = "parent" +parent = "root" +tag = "Parent" +position = [2.0, 0.0, 0.0] +model = "cube" +model_asset = "unit_cube" +model_size = [1.0, 1.0, 1.0] +color = 0x00FF00FF +shader_vs = "shaders/toon.vs" +shader_fs = "shaders/toon.fs" +shader_asset = "toon" +texture = "assets/textures/grid.png" +texture_asset = "grid" +uv_scale = [2.0, 2.0] +uv_offset = [0.0, 0.0] +material_asset = "toon_mat" + +[[entity]] +id = "child" +parent = "parent" +tag = "Child" +position = [0.0, 1.25, 0.0] +model = "sphere" +radius = 0.5 +color = 0xFF0000FF +shader_vs = "shaders/toon.vs" +shader_fs = "shaders/toon.fs" +``` + +### Supported Keys + +- `scene_script` (string) — scene-level script path (outside entity blocks) +- `id` (string) — unique identifier for parent links +- `parent` (string) — parent entity id +- `tag` (string) — `Tag` component value +- `position` (vec3) +- `rotation` (radians) or `rotation_deg` (degrees) +- `scale` (vec3) +- `model` — `cube`, `sphere`, `plane`, or file path +- `model_asset` (string) — asset key for models +- `model_id` (int) — numeric model id +- `model_size` (vec3) for cubes/planes +- `radius`, `rings`, `slices` for spheres +- `color` — hex `0xRRGGBBAA` (matches script colors) +- `outline_size` — optional model outline +- `shader_vs`, `shader_fs` — optional shader paths +- `shader_asset` (string) — asset key for shaders +- `shader_id` (int) — numeric shader id +- `texture` (string) — texture path +- `texture_asset` (string) — asset key for textures +- `texture_id` (int) — numeric texture id +- `material_asset` (string) — asset key for materials +- `material_id` (int) — numeric material id +- `uv_scale` (vec2) +- `uv_offset` (vec2) +- `uv_rotation` (radians) +- `script` (string) — per-entity script path +- `script_enabled` (bool) +- `script_class` (string) +- `script_var_*` (string) — per-field overrides for script-editable values + +## Loading a Scene + +```angelscript +uint root = Scene::Load("scenes/demo.toml"); +``` + +`Scene::Load` returns a root entity that parents all top-level entities. Use `ECS::IsValid(root)` to check success on the scene load. + +## Editor + +Run the app with `--editor` to open the ImGui editor. The **Scene** window shows the hierarchy tree, supports drag-and-drop reparenting, and lets you create/delete entities. The **Inspector** window shows components for the selected entity and allows live editing. diff --git a/docs/Scripting.md b/docs/Scripting.md new file mode 100644 index 0000000..0309cc5 --- /dev/null +++ b/docs/Scripting.md @@ -0,0 +1,62 @@ +# Scripting + +This page is a starting point for scripting documentation. + +## Overview +- Scripts live in `scripts/` and are executed by AngelScript. +- Predefined API and engine bindings are declared in `scripts/as.predefined`. + +## Input Actions +- Query input with `IsActionPressed`, `IsActionDown`, `IsActionReleased`, and `GetActionStrength`. +- Register and bind at runtime with `RegisterAction`, `BindKeyboard`, `BindMouseButton`, `BindGamepadButton`, and `BindGamepadAxis`. +- Enums: `Key`, `MouseButton`, `GamepadButton`, `GamepadAxis`. + +Example: + +```angelscript +RegisterAction("boost"); +BindKeyboard("boost", Key::LEFT_CONTROL); +BindGamepadButton("boost", 0, GamepadButton::RIGHT_TRIGGER_1); + +RegisterAction("zoom"); +BindGamepadAxis("zoom", 0, GamepadAxis::RIGHT_TRIGGER, 0.2f, false); +``` + +## ECS Texture + UV +- Texture and UV helpers live under the `ECS::` namespace. +- Texture: `AddTexture`, `SetTextureId`, `GetTextureId`, `HasTexture`, `RemoveTexture`. +- UV Transform: `AddUVTransform`, `SetUVScale`, `SetUVOffset`, `SetUVRotation`, `HasUVTransform`, `RemoveUVTransform`. + +Example: + +```angelscript +uint e = ECS::CreateEntity(); +ECS::AddTexture(e, texId); +ECS::AddUVTransform(e, 2.0f, 2.0f, 0.0f, 0.0f, 0.0f); +ECS::SetUVOffset(e, 0.25f, 0.0f); +``` + +## Asset Manager +- Asset loading can be keyed for editor and scene usage. +- Use `Asset::Load*` helpers to create assets and reference them by key. + +Example: + +```angelscript +Asset::LoadTexture("grid", "assets/textures/grid.png"); +Asset::LoadShader("toon", "shaders/toon.vs", "shaders/toon.fs"); +Asset::LoadCube("unit_cube", 1.0f, 1.0f, 1.0f); +Asset::CreateMaterial("toon_mat", Asset::GetShaderId("toon"), 255, 255, 255, 255); + +uint e = ECS::CreateEntity(); +ECS::AddModelRenderer(e, int(Asset::GetModelId("unit_cube")), 0xFFFFFFFF, 0.0f); +ECS::SetModelRendererShader(e, Asset::GetShaderId("toon")); +ECS::AddTexture(e, Asset::GetTextureId("grid")); +ECS::AddMaterial(e, Asset::GetMaterialId("toon_mat")); +``` + +## Examples +- `scripts/game.as` — demo showing ECS usage, rendering, scenegraph and input actions. + +## How to document APIs +Add Markdown pages in the `docs/` folder. Use relative links and include code fences for examples. diff --git a/docs/Usage.md b/docs/Usage.md new file mode 100644 index 0000000..2ed7f30 --- /dev/null +++ b/docs/Usage.md @@ -0,0 +1,100 @@ +# Usage + +This page documents how to build, run and maintain the Simian project and how to publish documentation to the Gitea wiki. + +## Prerequisites + +- Git +- CMake (>= 3.16; 3.25 recommended) +- A C++ toolchain (Visual Studio on Windows, GCC/Clang on Linux/macOS) +- Optional: SSH access to your Gitea instance (for wiki sync) + +If you just cloned the repository, initialise submodules: + +```bash +git submodule update --init --recursive +``` + +## Build (Windows / MSVC) + +```powershell +mkdir build +cd build +cmake -S .. -B . -G "Visual Studio 17 2022" -A x64 +cmake --build . --config Release +# Run +.\Release\simian.exe +``` + +## Build (Linux / macOS) + +```bash +mkdir build +cd build +cmake .. +cmake --build . --config Release +./simian +``` + +## Run tests + +From the project root (after configuring the build): + +```bash +cmake --build build --config Release --target RUN_TESTS +# or use CTest +ctest --test-dir build --output-on-failure +``` + +## Scripting / Hot-reload + +- Scripts live in the `scripts/` directory (e.g. `scripts/game.as`). +- The engine watches the scripts directory and will hot-reload scripts when changed. +- Predefined API is declared in `scripts/as.predefined`. + +## Documentation & Wiki + +Docs source lives in the `docs/` folder. The repository contains a CI workflow and a local script to mirror `docs/` to the Gitea wiki. + +Manual sync (local): + +```bash +bash scripts/sync_wiki.sh +``` + +CI sync (automated): + +1. The workflow `.gitea/workflows/sync-docs-to-wiki.yml` runs on pushes that affect `docs/**`. +2. The workflow requires an SSH private key in repository secrets named `WIKI_SSH_KEY`. + +To create the deploy key and CI secret: + +```bash +# Generate a keypair (do not commit the private key) +ssh-keygen -t ed25519 -C "simian-wiki-ci" -f simian_wiki_ci -N "" + +# Add simian_wiki_ci.pub as a deploy key in your Gitea repo: Settings → Deploy Keys +# Enable write/push access for that key. + +# Copy the private key (simian_wiki_ci) into your CI provider as secret WIKI_SSH_KEY. +``` + +Notes: +- The workflow clones the wiki remote `git@gitea.appstack.me:nick/simian.wiki.git` and pushes updates. +- If your Gitea host/paths differ, update `.github/workflows/sync-docs-to-wiki.yml` or the `scripts/sync_wiki.sh` script accordingly. + +## Adding / Updating Documentation + +1. Add or edit Markdown files under `docs/`. +2. Put images in `docs/images/` and reference them with relative paths, e.g. `![Alt](images/diagram.png)`. +3. Commit and push to the repo; CI will sync changes automatically (once secrets/deploy-key are configured). + +## Troubleshooting + +- If the CI job fails with authentication errors, confirm the deploy key was added to the Gitea repo and the private key is stored in the CI secrets as `WIKI_SSH_KEY`. +- To debug locally, run the `scripts/sync_wiki.sh` script and watch for `git push` errors. + +## Scenes + +Scenes are stored as TOML files in `scenes/`. Use `Scene::Load("scenes/demo.toml")` from scripts to create entities with hierarchy relationships. +See [Scenegraph](Scenegraph.md) for the format and examples. diff --git a/external/ImViewGuizmo/ImViewGuizmo.h b/external/ImViewGuizmo/ImViewGuizmo.h new file mode 100644 index 0000000..964ef5a --- /dev/null +++ b/external/ImViewGuizmo/ImViewGuizmo.h @@ -0,0 +1,603 @@ +#pragma once + +#include +#include + +/*=============================================== +ImViewGuizmo Single-Header Library by Marcel Kazemi + +To use, do this in one (and only one) of your C++ files: +#define IMVIEWGUIZMO_IMPLEMENTATION + #include "ImViewGuizmo.h" + +In all other files, just include the header as usual: +#include "ImViewGuizmo.h" + +Copyright (c) 2025 Marcel Kazemi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +======================================================*/ + +#ifndef IMVIEWGUIZMO_VEC3 + #define IMVIEWGUIZMO_USE_GLM_DEFAULTS + #ifndef GLM_ENABLE_EXPERIMENTAL + #define GLM_ENABLE_EXPERIMENTAL + #endif + #include + #include + #include + #include + + #define IMVIEWGUIZMO_VEC3 glm::vec3 + #define IMVIEWGUIZMO_VEC4 glm::vec4 + #define IMVIEWGUIZMO_QUAT glm::quat + #define IMVIEWGUIZMO_MAT4 glm::mat4 +#endif + +namespace ImViewGuizmo +{ + using vec3_t = IMVIEWGUIZMO_VEC3; + using vec4_t = IMVIEWGUIZMO_VEC4; + using quat_t = IMVIEWGUIZMO_QUAT; + using mat4_t = IMVIEWGUIZMO_MAT4; + +#ifdef IMVIEWGUIZMO_USE_GLM_DEFAULTS + namespace GizmoMath { + inline float get_vec_comp(const vec3_t& v, int index) { return v[index]; } + inline float get_vec_comp(const vec4_t& v, int index) { return v[index]; } + + inline vec3_t make_vec3(float x, float y, float z) { return vec3_t(x, y, z); } + inline vec4_t make_vec4(float x, float y, float z, float w) { return vec4_t(x, y, z, w); } + inline quat_t angleAxis(float angle, const vec3_t& axis) { return glm::angleAxis(angle, axis); } + inline quat_t quatLookAt(const vec3_t& dir, const vec3_t& up) { return glm::quatLookAt(dir, up); } + inline mat4_t mat4_identity() { return mat4_t(1.0f); } + + inline vec3_t cross(const vec3_t& a, const vec3_t& b) { return glm::cross(a, b); } + inline float dot(const vec3_t& a, const vec3_t& b) { return glm::dot(a, b); } + inline float dot(const quat_t& a, const quat_t& b) { return glm::dot(a, b); } + inline float length(const vec3_t& v) { return glm::length(v); } + inline float length2(const vec3_t& v) { return glm::length2(v); } + inline vec3_t normalize(const vec3_t& v) { return glm::normalize(v); } + inline vec3_t mix(const vec3_t& a, const vec3_t& b, float t) { return glm::mix(a, b, t); } + inline vec3_t add_vv(const vec3_t& a, const vec3_t& b) { return a + b; } + inline vec3_t subtract_vv(const vec3_t& a, const vec3_t& b) { return a - b; } + inline vec3_t multiply_vf(const vec3_t& v, float f) { return v * f; } + + inline mat4_t mat4_cast(const quat_t& q) { return glm::mat4_cast(q); } + inline mat4_t transpose(const mat4_t& m) { return glm::transpose(m); } + inline vec3_t get_matrix_col(const mat4_t& m, int col) { return vec3_t(m[col]); } + inline void set_matrix_col(mat4_t& m, int col, const vec4_t& v) { m[col] = v; } + + inline quat_t multiply_qq(const quat_t& a, const quat_t& b) { return a * b; } + inline vec3_t multiply_qv(const quat_t& q, const vec3_t& v) { return q * v; } + inline mat4_t multiply_mm(const mat4_t& a, const mat4_t& b) { return a * b; } + inline vec4_t multiply_mv4(const mat4_t& m, const vec4_t& v) { return m * v; } + } +#else + // User must provide a GizmoMath namespace implementing all functions if using custom types +#endif + + // INTERFACE + struct Style { + float scale = 1.f; + + // Axis visuals + float lineLength = 0.5f; + float lineWidth = 4.0f; + float circleRadius = 15.0f; + float fadeFactor = 0.25f; + + // Highlight + ImU32 highlightColor = IM_COL32(255, 255, 0, 255); + float highlightWidth = 2.0f; + + // Axis + ImU32 axisColors[3] = { + IM_COL32(230, 51, 51, 255), // X + IM_COL32(51, 230, 51, 255), // Y + IM_COL32(51, 128, 255, 255) // Z + }; + + // Labels + float labelSize = 1.0f; + const char* axisLabels[3] = {"X", "Y", "Z"}; + ImU32 labelColor = IM_COL32(255, 255, 255, 255); + + //Big Circle + float bigCircleRadius = 80.0f; + ImU32 bigCircleColor = IM_COL32(255, 255, 255, 50); + + // Animation + bool animateSnap = true; + float snapAnimationDuration = 0.5f; // in seconds + + // Zoom/Pan Button Visuals + float toolButtonRadius = 25.f; + float toolButtonInnerPadding = 4.f; + + ImU32 toolButtonColor = IM_COL32(144, 144, 144, 50); + ImU32 toolButtonHoveredColor = IM_COL32(215, 215, 215, 50); + ImU32 toolButtonIconColor = IM_COL32(215, 215, 215, 225); + }; + + // Constants + const vec3_t origin = GizmoMath::make_vec3(0.f, 0.f, 0.f); + const vec3_t worldRight = GizmoMath::make_vec3(1.f, 0.f, 0.f); // +X + const vec3_t worldUp = GizmoMath::make_vec3(0.f, -1.f, 0.f); // -Y + const vec3_t worldForward = GizmoMath::make_vec3(0.f, 0.f, 1.f); // +Z + const vec3_t axisVectors[3] = { GizmoMath::make_vec3(1,0,0), GizmoMath::make_vec3(0,1,0), GizmoMath::make_vec3(0,0,1) }; + static constexpr mat4_t gizmoProjectionMatrix = { + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, -0.01f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + + inline Style& GetStyle() { + static Style style; + return style; + } + + // Gizmo Axis Struct + struct GizmoAxis { + int id; // 0-5 for (+X,-X,+Y,-Y,+Z,-Z), 6=center + int axisIndex; // 0=X, 1=Y, 2=Z + float depth; // Screen-space depth + vec3_t direction; // 3D vector + }; + + enum ActiveTool { + TOOL_NONE, + TOOL_GIZMO, + TOOL_DOLLY, + TOOL_PAN + }; + + struct Context { + int hoveredAxisID = -1; + bool isZoomButtonHovered = false; + bool isPanButtonHovered = false; + ActiveTool activeTool = TOOL_NONE; + + // Animation state + bool isAnimating = false; + float animationStartTime = 0.f; + + vec3_t startPos; + vec3_t targetPos; + vec3_t startUp; + vec3_t targetUp; + + vec3_t animStartDir, animTargetDir; + float animStartDist, animTargetDist; + + void Reset() { + hoveredAxisID = -1; + isZoomButtonHovered = false; + isPanButtonHovered = false; + } + }; + + // Global context + inline Context& GetContext() { + static Context ctx; + return ctx; + } + + static float ImViewGuizmoLengthSqr(const ImVec2& v) { return v.x * v.x + v.y * v.y; } + static float mix(float a, float b, float t) { return a * (1.0f - t) + b * t; } + + inline void BeginFrame() { + static int lastFrame = -1; + const int currentFrame = ImGui::GetFrameCount(); + if (lastFrame != currentFrame) + lastFrame = currentFrame; GetContext().Reset(); + } + + inline bool IsUsing() { + return GetContext().activeTool != TOOL_NONE; + } + + inline bool IsOver() { + const Context& ctx = GetContext(); + return ctx.hoveredAxisID != -1 || ctx.isZoomButtonHovered || ctx.isPanButtonHovered; + } + + /// @brief Renders and handles the view gizmo logic. + /// @param cameraPos The position of the camera (will be modified). + /// @param cameraRot The rotation of the camera (will be modified). + /// @param pivot The position the camera will rotate around. + /// @param position The top-left screen position where the gizmo should be rendered. + /// @param rotationSpeed The rotation speed when dragging the gizmo. + /// @return True if the camera was modified, false otherwise. + bool Rotate(vec3_t& cameraPos, quat_t& cameraRot, const vec3_t& pivot, ImVec2 position, float rotationSpeed = 0.01f); + + /// @brief Renders a zoom button and handles its logic. + /// @param cameraPos The position of the camera (will be modified). + /// @param cameraRot The rotation of the camera (used for direction). + /// @param position The top-left screen position of the button. + /// @param zoomSpeed The speed/sensitivity of the zoom. + /// @return True if the camera was modified, false otherwise. + bool Dolly(vec3_t& cameraPos, const quat_t& cameraRot, ImVec2 position, float zoomSpeed = 0.05f); + + /// @brief Renders a pan button and handles its logic. + /// @param cameraPos The position of the camera (will be modified). + /// @param cameraRot The rotation of the camera (used for direction). + /// @param position The top-left screen position of the button. + /// @param panSpeed The speed/sensitivity of the pan. + /// @return True if the camera was modified, false otherwise. + bool Pan(vec3_t& cameraPos, const quat_t& cameraRot, ImVec2 position, float panSpeed = 0.01f); +} + +#ifdef IMVIEWGUIZMO_IMPLEMENTATION +namespace ImViewGuizmo { + + /// @brief Renders and handles the view gizmo logic. + inline bool Rotate(vec3_t& cameraPos, quat_t& cameraRot, const vec3_t& pivot, ImVec2 position, float rotationSpeed ) { + + auto& io = ImGui::GetIO(); + ImDrawList* drawList = ImGui::GetWindowDrawList(); + auto& ctx = GetContext(); + auto& style = GetStyle(); + bool wasModified = false; + + // Animation + if (ctx.isAnimating) { + float elapsedTime = static_cast(ImGui::GetTime()) - ctx.animationStartTime; + float t = std::min(1.0f, elapsedTime / style.snapAnimationDuration); + t = 1.0f - (1.0f - t) * (1.0f - t); // ease-out quad + + vec3_t currentDir = GizmoMath::normalize(GizmoMath::mix(ctx.animStartDir, ctx.animTargetDir, t)); + float currentDistance = mix(ctx.animStartDist, ctx.animTargetDist, t); + cameraPos = GizmoMath::add_vv(pivot, GizmoMath::multiply_vf(currentDir, currentDistance)); + + vec3_t currentUp = GizmoMath::normalize(GizmoMath::mix(ctx.startUp, ctx.targetUp, t)); + cameraRot = GizmoMath::quatLookAt(currentDir, currentUp); + + wasModified = true; + + if (t >= 1.0f) { + cameraPos = ctx.targetPos; + cameraRot = GizmoMath::quatLookAt(ctx.animTargetDir, ctx.targetUp); + ctx.isAnimating = false; + } + } + + // Gizmo sizes + const float gizmoDiameter = 256.f * style.scale; + const float halfGizmoSize = gizmoDiameter / 2.f; + const float scaledCircleRadius = style.circleRadius * style.scale; + const float scaledBigCircleRadius = style.bigCircleRadius * style.scale; + const float scaledLineWidth = style.lineWidth * style.scale; + const float scaledHighlightWidth = style.highlightWidth * style.scale; + const float scaledHighlightRadius = (style.circleRadius + 2.0f) * style.scale; + const float scaledFontSize = ImGui::GetFontSize() * style.scale * style.labelSize; + + // Gizmo view matrix (transpose of rotation) + mat4_t rotMat4 = GizmoMath::mat4_cast(cameraRot); + + vec3_t rcol0 = GizmoMath::get_matrix_col(rotMat4, 0); + vec3_t rcol1 = GizmoMath::get_matrix_col(rotMat4, 1); + vec3_t rcol2 = GizmoMath::get_matrix_col(rotMat4, 2); + + mat4_t gizmoViewMatrix = GizmoMath::mat4_identity(); + GizmoMath::set_matrix_col(gizmoViewMatrix, 0, GizmoMath::make_vec4(rcol0.x, rcol1.x, rcol2.x, 0.0f)); + GizmoMath::set_matrix_col(gizmoViewMatrix, 1, GizmoMath::make_vec4(rcol0.y, rcol1.y, rcol2.y, 0.0f)); + GizmoMath::set_matrix_col(gizmoViewMatrix, 2, GizmoMath::make_vec4(rcol0.z, rcol1.z, rcol2.z, 0.0f)); + GizmoMath::set_matrix_col(gizmoViewMatrix, 3, GizmoMath::make_vec4(0.0f, 0.0f, 0.0f, 1.0f)); + + mat4_t gizmoMvp = GizmoMath::multiply_mm(gizmoProjectionMatrix, gizmoViewMatrix); + + // Axes (same ordering/depth calc as glm version) + std::array axes; + + vec3_t xAxisInView = GizmoMath::get_matrix_col(gizmoViewMatrix, 0); + vec3_t yAxisInView = GizmoMath::get_matrix_col(gizmoViewMatrix, 1); + vec3_t zAxisInView = GizmoMath::get_matrix_col(gizmoViewMatrix, 2); + + axes[0] = {0, 0, xAxisInView.z, axisVectors[0]}; + axes[1] = {1, 0, -xAxisInView.z, GizmoMath::multiply_vf(axisVectors[0], -1.0f)}; + axes[2] = {2, 1, yAxisInView.z, axisVectors[1]}; + axes[3] = {3, 1, -yAxisInView.z, GizmoMath::multiply_vf(axisVectors[1], -1.0f)}; + axes[4] = {4, 2, zAxisInView.z, axisVectors[2]}; + axes[5] = {5, 2, -zAxisInView.z, GizmoMath::multiply_vf(axisVectors[2], -1.0f)}; + + std::sort(axes.begin(), axes.end(), [](const GizmoAxis& a, const GizmoAxis& b) { + return a.depth < b.depth; + }); + + // world->screen + auto worldToScreen = [&](const vec3_t& worldPos) -> ImVec2 { + const vec4_t clipPos = GizmoMath::multiply_mv4(gizmoMvp, GizmoMath::make_vec4(worldPos.x, worldPos.y, worldPos.z, 1.0f)); + const float w = clipPos.w; + if (fabsf(w) < 1e-6f) + return {-FLT_MAX, -FLT_MAX}; + const float ndcX = clipPos.x / w; + const float ndcY = clipPos.y / w; + return {position.x + ndcX * halfGizmoSize, position.y - ndcY * halfGizmoSize}; + }; + + const ImVec2 originScreenPos = worldToScreen(origin); + + // Hover detection + const bool canInteract = !(io.ConfigFlags & ImGuiConfigFlags_NoMouse); + if (canInteract && ctx.activeTool == TOOL_NONE && !ctx.isAnimating) { + ImVec2 mousePos = io.MousePos; + float distToCenterSq = ImViewGuizmoLengthSqr(ImVec2(mousePos.x - position.x, mousePos.y - position.y)); + + if (distToCenterSq < (halfGizmoSize + scaledCircleRadius) * (halfGizmoSize + scaledCircleRadius)) { + const float minDistanceSq = scaledCircleRadius * scaledCircleRadius; + for (const auto& axis : axes) { + if (axis.depth < -0.1f) + continue; + + ImVec2 handlePos = worldToScreen(GizmoMath::multiply_vf(axis.direction, style.lineLength)); + if (ImViewGuizmoLengthSqr(ImVec2(handlePos.x - mousePos.x, handlePos.y - mousePos.y)) < minDistanceSq) + ctx.hoveredAxisID = axis.id; + } + if (ctx.hoveredAxisID == -1 && + ImViewGuizmoLengthSqr(ImVec2(originScreenPos.x - mousePos.x, originScreenPos.y - mousePos.y)) < scaledBigCircleRadius * scaledBigCircleRadius) + ctx.hoveredAxisID = 6; + } + } + + // Drawing + if (ctx.hoveredAxisID == 6 || ctx.activeTool == TOOL_GIZMO) + drawList->AddCircleFilled(originScreenPos, scaledBigCircleRadius, style.bigCircleColor); + + ImFont* font = ImGui::GetFont(); + for (const auto& axis : axes) { + float colorFactor = mix(style.fadeFactor, 1.0f, (axis.depth + 1.0f) * 0.5f); + ImVec4 baseColor = ImGui::ColorConvertU32ToFloat4(style.axisColors[axis.axisIndex]); + baseColor.w *= colorFactor; + ImU32 final_color = ImGui::ColorConvertFloat4ToU32(baseColor); + + const ImVec2 handlePos = worldToScreen(GizmoMath::multiply_vf(axis.direction, style.lineLength)); + + ImVec2 lineDir = {handlePos.x - originScreenPos.x, handlePos.y - originScreenPos.y}; + float lineLengthVal = sqrtf(lineDir.x * lineDir.x + lineDir.y * lineDir.y) + 1e-6f; + lineDir.x /= lineLengthVal; lineDir.y /= lineLengthVal; + ImVec2 lineEndPos = {handlePos.x - lineDir.x * scaledCircleRadius, handlePos.y - lineDir.y * scaledCircleRadius}; + + drawList->AddLine(originScreenPos, lineEndPos, final_color, scaledLineWidth); + drawList->AddCircleFilled(handlePos, scaledCircleRadius, final_color); + + if (ctx.hoveredAxisID == axis.id) + drawList->AddCircle(handlePos, scaledHighlightRadius, style.highlightColor, 0, scaledHighlightWidth); + + float textFactor = std::max(0.0f, std::min(1.0f, 1.0f + axis.depth * 2.5f)); + if (textFactor > 0.01f) { + ImVec4 textColor = ImGui::ColorConvertU32ToFloat4(style.labelColor); + textColor.w *= textFactor; + const char* label = style.axisLabels[axis.axisIndex]; + ImVec2 textSize = font->CalcTextSizeA(scaledFontSize, FLT_MAX, 0.f, label); + drawList->AddText(font, scaledFontSize, {handlePos.x - textSize.x * 0.5f, handlePos.y - textSize.y * 0.5f}, ImGui::ColorConvertFloat4ToU32(textColor), label); + } + } + + // Drag start + if (canInteract && io.MouseDown[0] && ctx.activeTool == TOOL_NONE && ctx.hoveredAxisID == 6) { + ctx.activeTool = TOOL_GIZMO; + ctx.isAnimating = false; + } + + // Active tool rotation + if (ctx.activeTool == TOOL_GIZMO) { + float yawAngle = -io.MouseDelta.x * rotationSpeed; + float pitchAngle = -io.MouseDelta.y * rotationSpeed; + + quat_t yawRotation = GizmoMath::angleAxis(yawAngle, worldUp); + vec3_t rightAxis = GizmoMath::multiply_qv(cameraRot, worldRight); + quat_t pitchRotation = GizmoMath::angleAxis(pitchAngle, rightAxis); + quat_t totalRotation = GizmoMath::multiply_qq(yawRotation, pitchRotation); + + vec3_t relativeCamPos = GizmoMath::subtract_vv(cameraPos, pivot); + cameraPos = GizmoMath::add_vv(pivot, GizmoMath::multiply_qv(totalRotation, relativeCamPos)); + cameraRot = GizmoMath::multiply_qq(totalRotation, cameraRot); + + wasModified = true; + } + + // Snap on release + if (canInteract && ImGui::IsMouseReleased(0) && ctx.hoveredAxisID >= 0 && ctx.hoveredAxisID <= 5 && ctx.activeTool == TOOL_NONE) { + int axisIndex = ctx.hoveredAxisID / 2; + float sign = (ctx.hoveredAxisID % 2 == 0) ? -1.0f : 1.0f; + vec3_t targetDir = GizmoMath::multiply_vf(axisVectors[axisIndex], sign); + + float currentDistance = GizmoMath::length(GizmoMath::subtract_vv(cameraPos, pivot)); + vec3_t targetPosition = GizmoMath::add_vv(pivot, GizmoMath::multiply_vf(targetDir, currentDistance)); + + vec3_t dirNormalized = GizmoMath::normalize(targetDir); + + vec3_t targetUp = GizmoMath::multiply_vf(worldUp, -1.0f); + // If dir is nearly parallel to worldUp, pick a different up to avoid flipping + if (fabsf(GizmoMath::dot(dirNormalized, targetUp)) > 0.999f) + if (dirNormalized.y > 0.0f) // facing "up" + targetUp = worldForward; + else // facing "down" + targetUp = GizmoMath::multiply_vf(worldForward, -1.0f); + + quat_t targetRotation = GizmoMath::quatLookAt(targetDir, targetUp); + + if (style.animateSnap && style.snapAnimationDuration > 0.0f) { + bool pos_is_different = GizmoMath::length2(GizmoMath::subtract_vv(cameraPos, targetPosition)) > 0.0001f; + bool rot_is_different = (1.0f - fabsf(GizmoMath::dot(cameraRot, targetRotation))) > 0.0001f; + + if (pos_is_different || rot_is_different) { + ctx.isAnimating = true; + ctx.animationStartTime = static_cast(ImGui::GetTime()); + ctx.startPos = cameraPos; + ctx.targetPos = targetPosition; + ctx.startUp = GizmoMath::multiply_qv(cameraRot, GizmoMath::multiply_vf(worldUp, -1.0f)); + ctx.targetUp = targetUp; + + ctx.animStartDist = GizmoMath::length(GizmoMath::subtract_vv(ctx.startPos, pivot)); + ctx.animTargetDist = GizmoMath::length(GizmoMath::subtract_vv(ctx.targetPos, pivot)); + + if (ctx.animStartDist > 0.0001f) + ctx.animStartDir = GizmoMath::normalize(GizmoMath::subtract_vv(ctx.startPos, pivot)); + else + ctx.animStartDir = worldForward; + + if (ctx.animTargetDist > 0.0001f) + ctx.animTargetDir = GizmoMath::normalize(GizmoMath::subtract_vv(ctx.targetPos, pivot)); + } + } else { + cameraRot = targetRotation; + cameraPos = targetPosition; + wasModified = true; + } + } + + if (!io.MouseDown[0] && ctx.activeTool != TOOL_NONE) + ctx.activeTool = TOOL_NONE; + + return wasModified; + } + + /// @brief Renders a zoom button and handles its logic. + inline bool Dolly(vec3_t& cameraPos, const quat_t& cameraRot, const ImVec2 position, const float zoomSpeed) { + + const ImGuiIO& io = ImGui::GetIO(); + ImDrawList* drawList = ImGui::GetWindowDrawList(); + auto& ctx = GetContext(); + const Style& style = GetStyle(); + bool wasModified = false; + + const bool canInteract = !(io.ConfigFlags & ImGuiConfigFlags_NoMouse); + const float radius = style.toolButtonRadius * style.scale; + const ImVec2 center = { position.x + radius, position.y + radius }; + + bool isHovered = false; + if (canInteract && (ctx.activeTool == TOOL_NONE || ctx.activeTool == TOOL_DOLLY)) { + if (ImViewGuizmoLengthSqr({io.MousePos.x - center.x, io.MousePos.y - center.y}) < radius * radius) { + isHovered = true; + } + } + ctx.isZoomButtonHovered = isHovered; + + if (canInteract && (isHovered || ctx.activeTool == TOOL_DOLLY)) { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeNS); + } + + if (canInteract && isHovered && io.MouseDown[0] && ctx.activeTool == TOOL_NONE) { + ctx.activeTool = TOOL_DOLLY; + ctx.isAnimating = false; + } + + if (ctx.activeTool == TOOL_DOLLY && io.MouseDelta.y != 0.0f) { + vec3_t forwardMovement = GizmoMath::multiply_vf( + GizmoMath::multiply_qv(cameraRot, worldForward), + -io.MouseDelta.y * zoomSpeed + ); + cameraPos = GizmoMath::add_vv(cameraPos, forwardMovement); + wasModified = true; + } + + const ImU32 bgColor = (ctx.activeTool == TOOL_DOLLY || isHovered) ? style.toolButtonHoveredColor : style.toolButtonColor; + drawList->AddCircleFilled(center, radius, bgColor); + + const float p = style.toolButtonInnerPadding * style.scale; + const float th = 2.0f * style.scale; + const ImU32 iconColor = style.toolButtonIconColor; + constexpr float iconScale = 0.5f; + const float scaledP = p * iconScale; + const float scaledRadius = radius * iconScale; + + ImVec2 glassCenter = { center.x - scaledP / 2.0f, center.y - scaledP / 2.0f }; + const float glassRadius = scaledRadius - scaledP; + drawList->AddCircle(glassCenter, glassRadius, iconColor, 0, th); + + const ImVec2 handleStart = { center.x + scaledRadius / 2.0f, center.y + scaledRadius / 2.0f }; + const ImVec2 handleEnd = { center.x + scaledRadius - (p * iconScale), center.y + scaledRadius - (p * iconScale) }; + drawList->AddLine(handleStart, handleEnd, iconColor, th); + + const float plusHalfSize = glassRadius * 0.5f; + drawList->AddLine({glassCenter.x, glassCenter.y - plusHalfSize}, {glassCenter.x, glassCenter.y + plusHalfSize}, iconColor, th); + drawList->AddLine({glassCenter.x - plusHalfSize, glassCenter.y}, {glassCenter.x + plusHalfSize, glassCenter.y}, iconColor, th); + + return wasModified; + } + + /// @brief Renders a pan button and handles its logic. + inline bool Pan(vec3_t& cameraPos, const quat_t& cameraRot, const ImVec2 position, const float panSpeed) { + + const ImGuiIO& io = ImGui::GetIO(); + ImDrawList* drawList = ImGui::GetWindowDrawList(); + auto& ctx = GetContext(); + const Style& style = GetStyle(); + bool wasModified = false; + + const vec3_t localWorldRight = GizmoMath::make_vec3(1.0f, 0.0f, 0.0f); + const vec3_t localWorldUp = GizmoMath::make_vec3(0.0f, -1.0f, 0.0f); + const bool canInteract = !(io.ConfigFlags & ImGuiConfigFlags_NoMouse); + const float radius = style.toolButtonRadius * style.scale; + const ImVec2 center = { position.x + radius, position.y + radius }; + + bool isHovered = false; + if (canInteract && (ctx.activeTool == TOOL_NONE || ctx.activeTool == TOOL_PAN)) { + if (ImViewGuizmoLengthSqr({io.MousePos.x - center.x, io.MousePos.y - center.y}) < radius * radius) { + isHovered = true; + } + } + ctx.isPanButtonHovered = isHovered; + + if (canInteract && (isHovered || ctx.activeTool == TOOL_PAN)) { + ImGui::SetMouseCursor(ImGuiMouseCursor_ResizeAll); + } + + if (canInteract && isHovered && io.MouseDown[0] && ctx.activeTool == TOOL_NONE) { + ctx.activeTool = TOOL_PAN; + ctx.isAnimating = false; + } + + if (ctx.activeTool == TOOL_PAN && (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f)) { + vec3_t rightMovement = GizmoMath::multiply_vf(GizmoMath::multiply_qv(cameraRot, localWorldRight), -io.MouseDelta.x * panSpeed); + vec3_t upMovement = GizmoMath::multiply_vf(GizmoMath::multiply_qv(cameraRot, localWorldUp), io.MouseDelta.y * panSpeed); + cameraPos = GizmoMath::add_vv(cameraPos, rightMovement); + cameraPos = GizmoMath::add_vv(cameraPos, upMovement); + wasModified = true; + } + + const ImU32 bgColor = (isHovered || ctx.activeTool == TOOL_PAN) ? style.toolButtonHoveredColor : style.toolButtonColor; + drawList->AddCircleFilled(center, radius, bgColor); + + const ImU32 iconColor = style.toolButtonIconColor; + const float th = 2.0f * style.scale; + const float size = radius * 0.5f; + const float arm = size * 0.25f; + + const ImVec2 topTip = { center.x, center.y - size }; + drawList->AddLine({ topTip.x - arm, topTip.y + arm }, topTip, iconColor, th); + drawList->AddLine({ topTip.x + arm, topTip.y + arm }, topTip, iconColor, th); + const ImVec2 botTip = { center.x, center.y + size }; + drawList->AddLine({ botTip.x - arm, botTip.y - arm }, botTip, iconColor, th); + drawList->AddLine({ botTip.x + arm, botTip.y - arm }, botTip, iconColor, th); + const ImVec2 leftTip = { center.x - size, center.y }; + drawList->AddLine({ leftTip.x + arm, leftTip.y - arm }, leftTip, iconColor, th); + drawList->AddLine({ leftTip.x + arm, leftTip.y + arm }, leftTip, iconColor, th); + const ImVec2 rightTip = { center.x + size, center.y }; + drawList->AddLine({ rightTip.x - arm, rightTip.y - arm }, rightTip, iconColor, th); + drawList->AddLine({ rightTip.x - arm, rightTip.y + arm }, rightTip, iconColor, th); + + return wasModified; + } + +} // namespace ImViewGuizmo +#endif // IMVIEWGUIZMO_IMPLEMENTATION \ No newline at end of file diff --git a/external/acutest b/external/acutest deleted file mode 160000 index 31751b4..0000000 --- a/external/acutest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 31751b4089c93b46a9fd8a8183a695f772de66de diff --git a/external/physfs b/external/physfs new file mode 160000 index 0000000..49cfd5f --- /dev/null +++ b/external/physfs @@ -0,0 +1 @@ +Subproject commit 49cfd5fdd46f38b4c2611556b44ee9b0d308e162 diff --git a/fonts/Inter-Regular.ttf b/fonts/Inter-Regular.ttf new file mode 100644 index 0000000..ce097c8 Binary files /dev/null and b/fonts/Inter-Regular.ttf differ diff --git a/fonts/JetBrainsMono-Regular.ttf b/fonts/JetBrainsMono-Regular.ttf new file mode 100644 index 0000000..dff66cc Binary files /dev/null and b/fonts/JetBrainsMono-Regular.ttf differ diff --git a/fonts/fa-regular-400.ttf b/fonts/fa-regular-400.ttf new file mode 100644 index 0000000..d75d4c6 Binary files /dev/null and b/fonts/fa-regular-400.ttf differ diff --git a/fonts/fa-solid-900.ttf b/fonts/fa-solid-900.ttf new file mode 100644 index 0000000..a041418 Binary files /dev/null and b/fonts/fa-solid-900.ttf differ diff --git a/imgui.ini b/imgui.ini index 074a4e9..74bdeae 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,34 +9,111 @@ Size=400,400 Collapsed=0 [Window][Game Window] -Pos=8,29 -Size=1264,513 +Pos=232,28 +Size=663,507 Collapsed=0 -DockId=0x00000001,0 +DockId=0x00000004,0 [Window][Log Viewer] -Pos=8,544 -Size=1264,168 +Pos=8,537 +Size=1264,175 Collapsed=0 DockId=0x00000002,0 [Window][##TOAST0] -Pos=1010,635 -Size=250,65 +Pos=1035,637 +Size=225,63 Collapsed=0 [Window][##TOAST1] -Pos=1010,543 -Size=250,65 +Pos=1035,564 +Size=225,63 Collapsed=0 [Window][##TOAST2] -Pos=982,485 -Size=278,65 +Pos=1035,491 +Size=225,63 Collapsed=0 -[Docking][Data] -DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,29 Size=1264,683 Split=Y Selected=0x27A02DAA - DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,513 CentralNode=1 Selected=0x27A02DAA - DockNode ID=0x00000002 Parent=0x9076BACA SizeRef=1264,168 Selected=0xBEDDA0C1 +[Window][##TOAST3] +Pos=1675,741 +Size=225,63 +Collapsed=0 + +[Window][##TOAST4] +Pos=1068,345 +Size=192,63 +Collapsed=0 + +[Window][Scene (0 entities)] +Pos=60,60 +Size=32,36 +Collapsed=1 + +[Window][Comic Engine] +Pos=60,60 +Size=387,337 +Collapsed=0 + +[Window][Scene (1 entities)] +Pos=60,60 +Size=71,56 +Collapsed=0 + +[Window][Scene (2 entities)] +Pos=60,60 +Size=71,82 +Collapsed=0 + +[Window][Scene (3 entities)] +Pos=60,60 +Size=71,108 +Collapsed=0 + +[Window][##TOAST5] +Pos=1071,272 +Size=189,63 +Collapsed=0 + +[Window][Inspector] +Pos=897,28 +Size=375,507 +Collapsed=0 +DockId=0x00000006,0 + +[Window][Scene] +Pos=8,28 +Size=222,507 +Collapsed=0 +DockId=0x00000003,0 + +[Window][New Scene] +Pos=512,318 +Size=255,84 +Collapsed=0 + +[Window][Save Scene] +Pos=807,454 +Size=306,82 +Collapsed=0 + +[Window][Load Scene] +Pos=807,454 +Size=306,82 +Collapsed=0 + +[Window][Assets] +Pos=8,761 +Size=1904,274 +Collapsed=0 +DockId=0x00000002,1 + +[Docking][Data] +DockSpace ID=0x9076BACA Window=0x34F970D7 Pos=8,28 Size=1264,684 Split=Y Selected=0x27A02DAA + DockNode ID=0x00000001 Parent=0x9076BACA SizeRef=1264,507 Split=X + DockNode ID=0x00000005 Parent=0x00000001 SizeRef=887,684 Split=X + DockNode ID=0x00000003 Parent=0x00000005 SizeRef=222,684 Selected=0xE601B12F + DockNode ID=0x00000004 Parent=0x00000005 SizeRef=663,684 CentralNode=1 Selected=0x27A02DAA + DockNode ID=0x00000006 Parent=0x00000001 SizeRef=375,684 Selected=0x36DC96AB + DockNode ID=0x00000002 Parent=0x9076BACA SizeRef=1264,175 Selected=0xBEDDA0C1 diff --git a/include/Application.h b/include/Application.h index 9defe7d..b840529 100644 --- a/include/Application.h +++ b/include/Application.h @@ -1,8 +1,17 @@ #pragma once -#include "ScriptEngine.h" +#include "scripting/ScriptEngine.h" #include "HotReload.h" -#include "GuiManager.h" +#include "gui/GuiManager.h" #include "raylib.h" +#include "ShaderManager.h" +#include "ModelManager.h" +#include "MaterialManager.h" +#include "AssetManager.h" +#include "TextureManager.h" +#include "InputManager.h" +#include +#include +#include class Application { public: @@ -12,25 +21,85 @@ public: bool Initialize(int argc, char* argv[]); void Run(); void Shutdown(); + void RequestShutdown() { queueShutdown = true; } bool IsEditorEnabled() const { return enableEditor; } bool HasScriptCompilationError() const { return scriptCompilationError; } + entt::registry& GetRegistry() { return registry; } + AssetManager& GetAssetManager() { return assetManager; } + InputManager& GetInputManager() { return inputManager; } + Camera3D& GetEditorCamera() { return editorCamera; } + bool IsUsingEditorCamera() const { return useEditorCamera; } + void SetUseEditorCamera(bool enabled) { useEditorCamera = enabled; } private: + struct EntityScriptModule { + std::string path; + std::string moduleName; + asIScriptModule* module = nullptr; + // For object-instance based entity scripts we cache the object type + // (stored opaquely here to avoid requiring the AngelScript headers + // in all translation units) and the object-methods (Init/Update/Shutdown) + // to avoid repeated lookups. + asITypeInfo* objectType = nullptr; + std::string objectTypeName; + asIScriptFunction* initFunc = nullptr; // object method: Init(uint) + asIScriptFunction* updateFunc = nullptr; // object method: Update(uint,float) + asIScriptFunction* shutdownFunc = nullptr;// object method: Shutdown(uint) + bool valid = false; + }; + + struct SceneScriptInstance { + asIScriptObject* instance = nullptr; + std::string className; + asIScriptFunction* initFunc = nullptr; + asIScriptFunction* updateFunc = nullptr; + asIScriptFunction* shutdownFunc = nullptr; + }; + ScriptEngine scriptEngine; HotReload* hotReload; bool scriptCompilationError; - FILE* logFile; + struct PHYSFS_File* logFile; GuiManager guiManager; bool enableEditor; + bool queueShutdown = false; RenderTexture2D renderTexture; // renderTexture for Raylib rendering + // ECS + entt::registry registry; + + // Resource managers + ShaderManager shaderManager; + ModelManager modelManager; + MaterialManager materialManager; + AssetManager assetManager; + TextureManager textureManager; + InputManager inputManager; + std::unordered_map entityScriptModules; + std::unordered_map entityScriptBindings; + unsigned int entityScriptModuleCounter = 0; + std::string currentSceneScriptPath; + SceneScriptInstance sceneScriptInstance; + Camera3D editorCamera; + bool useEditorCamera = false; + static const int WINDOW_WIDTH = 1280; static const int WINDOW_HEIGHT = 720; static const int TARGET_FPS = 60; static const char* WINDOW_TITLE; - static const char* SCRIPT_FILE; void Update(float deltaTime); + void UpdateSystems(float deltaTime); + void UpdateEntityScripts(float deltaTime); + void ShutdownEntityScripts(); + bool ProcessSceneScriptChanges(); + void ReloadEntityScriptModules(); + EntityScriptModule* GetEntityScriptModule(const std::string& path); + void InitializeSceneScriptInstance(); + void ShutdownSceneScriptInstance(); + void CallSceneUpdate(float deltaTime); + void RenderScene(); void Draw(); + bool RunAutoloadScript(bool abortOnFailure); }; \ No newline at end of file diff --git a/include/AssetManager.h b/include/AssetManager.h new file mode 100644 index 0000000..9bc35c4 --- /dev/null +++ b/include/AssetManager.h @@ -0,0 +1,144 @@ +#pragma once + +#include "raylib.h" +#include +#include +#include + +class ModelManager; +class ShaderManager; +class MaterialManager; +class TextureManager; + +struct AssetEntry { + unsigned int id = 0; + std::string source; +}; + +struct TextureDefinition { + std::string path; +}; + +struct ShaderDefinition { + std::string vsPath; + std::string fsPath; +}; + +struct ModelDefinition { + enum class Type { + Path, + Primitive, + Heightmap, + Cubicmap, + Mesh + }; + + Type type = Type::Path; + std::string path; + std::string primitive; + std::vector params; +}; + +struct MaterialDefinition { + std::string shaderKey; + unsigned int shaderId = 0; + Color albedo = {255, 255, 255, 255}; + bool hasShaderKey = false; + bool hasShaderId = false; +}; + +class AssetManager +{ +public: + AssetManager(); + ~AssetManager(); + + void SetManagers(ModelManager* modelMgr, ShaderManager* shaderMgr, + MaterialManager* materialMgr, TextureManager* textureMgr); + + // Register an existing asset ID under a key (no loading performed). + bool RegisterTexture(const std::string& key, unsigned int id, const std::string& source = std::string()); + bool RegisterShader(const std::string& key, unsigned int id, const std::string& source = std::string()); + bool RegisterModel(const std::string& key, unsigned int id, const std::string& source = std::string()); + bool RegisterMaterial(const std::string& key, unsigned int id, const std::string& source = std::string()); + + // Load helpers that also register by key. + unsigned int LoadTexture(const std::string& key, const std::string& path); + unsigned int LoadShader(const std::string& key, const std::string& vsPath, const std::string& fsPath); + unsigned int LoadModel(const std::string& key, const std::string& path); + unsigned int LoadCube(const std::string& key, float width, float height, float length); + unsigned int LoadPoly(const std::string& key, int sides, float radius); + unsigned int LoadSphere(const std::string& key, float radius, int rings, int slices); + unsigned int LoadHemiSphere(const std::string& key, float radius, int rings, int slices); + unsigned int LoadPlane(const std::string& key, float width, float length, int resX, int resZ); + unsigned int LoadCylinder(const std::string& key, float radius, float height, int slices); + unsigned int LoadCone(const std::string& key, float radius, float height, int slices); + unsigned int LoadTorus(const std::string& key, float radius, float size, int radSeg, int sides); + unsigned int LoadKnot(const std::string& key, float radius, float size, int radSeg, int sides); + unsigned int LoadHeightmap(const std::string& key, const std::string& heightmapPath, float sizeX, float sizeY, float sizeZ); + unsigned int LoadCubicmap(const std::string& key, const std::string& cubicmapPath, float sizeX, float sizeY, float sizeZ); + unsigned int LoadFromMesh(const std::string& key, Mesh mesh, const std::string& source = std::string()); + unsigned int CreateMaterial(const std::string& key, unsigned int shaderId, Color albedo); + bool ApplyMeshTangents(const std::string& key, int meshIndex = 0); + + // Unload by key (removes registry entries). + bool UnloadTexture(const std::string& key); + bool UnloadShader(const std::string& key); + bool UnloadModel(const std::string& key); + bool UnloadMaterial(const std::string& key); + + // Query by key. + unsigned int GetTextureId(const std::string& key) const; + unsigned int GetShaderId(const std::string& key) const; + unsigned int GetModelId(const std::string& key) const; + unsigned int GetMaterialId(const std::string& key) const; + + // Query by id. + const std::string& GetTextureKey(unsigned int id) const; + const std::string& GetShaderKey(unsigned int id) const; + const std::string& GetModelKey(unsigned int id) const; + const std::string& GetMaterialKey(unsigned int id) const; + + // Asset listing for editor UI. + std::vector ListTextureKeys() const; + std::vector ListShaderKeys() const; + std::vector ListModelKeys() const; + std::vector ListMaterialKeys() const; + + // Source info (path or descriptive string). + std::string GetTextureSource(const std::string& key) const; + std::string GetShaderSource(const std::string& key) const; + std::string GetModelSource(const std::string& key) const; + std::string GetMaterialSource(const std::string& key) const; + + // Asset registry persistence. + bool SaveRegistry(const std::string& path) const; + bool LoadRegistry(const std::string& path); + + // Clear registry entries (does not unload underlying resources). + void Clear(); + +private: + ModelManager* modelManager = nullptr; + ShaderManager* shaderManager = nullptr; + MaterialManager* materialManager = nullptr; + TextureManager* textureManager = nullptr; + + std::unordered_map textureAssets; + std::unordered_map shaderAssets; + std::unordered_map modelAssets; + std::unordered_map materialAssets; + + std::unordered_map textureDefs; + std::unordered_map shaderDefs; + std::unordered_map modelDefs; + std::unordered_map materialDefs; + + std::unordered_map textureIds; + std::unordered_map shaderIds; + std::unordered_map modelIds; + std::unordered_map materialIds; + + const std::string& GetKeyForId(const std::unordered_map& map, unsigned int id) const; + std::vector ListKeys(const std::unordered_map& map) const; +}; diff --git a/include/ECSComponents.h b/include/ECSComponents.h new file mode 100644 index 0000000..46e018c --- /dev/null +++ b/include/ECSComponents.h @@ -0,0 +1,182 @@ +#pragma once +#include "raylib.h" +#include "raymath.h" +#include "entt.hpp" +#include +#include + +// ECSTransform component - position, rotation, scale, and cached local matrix +struct ECSTransform { + Vector3 position; + Vector3 rotation; // Euler rotation (radians) + Vector3 scale; + Matrix localMatrix; + + ECSTransform() + : position{0.0f, 0.0f, 0.0f} + , rotation{0.0f, 0.0f, 0.0f} + , scale{1.0f, 1.0f, 1.0f} + , localMatrix(MatrixIdentity()) {} + + ECSTransform(Vector3 pos, Vector3 scl, Vector3 rot) + : position(pos), rotation(rot), scale(scl), localMatrix(MatrixIdentity()) {} + + ECSTransform(Vector3 pos, Vector3 scl = {1.0f, 1.0f, 1.0f}, float rotY = 0.0f) + : position(pos) + , rotation{0.0f, rotY, 0.0f} + , scale(scl) + , localMatrix(MatrixIdentity()) {} +}; + +// Velocity component - for movement +struct Velocity { + Vector3 linear; // Linear velocity (units per second) + float angular; // Angular velocity (radians per second) + + Velocity() + : linear{0.0f, 0.0f, 0.0f} + , angular(0.0f) {} + + Velocity(Vector3 lin, float ang = 0.0f) + : linear(lin), angular(ang) {} +}; + +// ModelRenderer component - 3D model rendering data +struct ModelRenderer { + size_t modelId; // Index into models array + Color color; // Tint color + float outlineSize; // Outline thickness + unsigned int shaderId; // Shader to use (0 = no shader) + + ModelRenderer() + : modelId(0) + , color(WHITE) + , outlineSize(0.0f) + , shaderId(0) {} + + ModelRenderer(int model, Color col = WHITE, float outline = 0.0f, unsigned int shader = 0) + : modelId(model), color(col), outlineSize(outline), shaderId(shader) {} +}; + +// Texture component - material texture reference +struct TextureComponent { + unsigned int textureId; + + TextureComponent() : textureId(0) {} + TextureComponent(unsigned int id) : textureId(id) {} +}; + +// UV transform component - tiling and offset for texture sampling +struct UVTransform { + Vector2 scale; + Vector2 offset; + float rotation; + + UVTransform() + : scale{1.0f, 1.0f} + , offset{0.0f, 0.0f} + , rotation(0.0f) {} + + UVTransform(Vector2 s, Vector2 o = {0.0f, 0.0f}, float r = 0.0f) + : scale(s), offset(o), rotation(r) {} +}; + +// Tag component - simple string identifier +struct Tag { + std::string name; + + Tag() : name("") {} + Tag(const std::string& n) : name(n) {} +}; + +// ScriptComponent - per-entity script reference +struct ScriptComponent { + std::string scriptPath; + bool enabled = true; + bool started = false; + // Optional: name of the script class to instantiate. If empty, engine + // will attempt to pick a reasonable object type from the module. + std::string className; + // Opaque pointer to the script object instance (asIScriptObject*). + void* scriptInstance = nullptr; + // Persisted overrides for editable script fields (name -> raw value string). + std::unordered_map scriptOverrides; +}; + +// SceneEntity component - marks entities owned by the scene +struct SceneEntity { + bool persistent = false; + std::string id; +}; + +// Hierarchy component - parent/child links using EnTT-friendly linked list +struct Hierarchy { + entt::entity parent{entt::null}; + entt::entity first{entt::null}; + entt::entity prev{entt::null}; + entt::entity next{entt::null}; +}; + +// WorldTransform component - computed from hierarchy + local transform +struct WorldTransform { + Vector3 position; + Vector3 rotation; // Euler rotation (radians) + Vector3 scale; + Matrix worldMatrix; + + WorldTransform() + : position{0.0f, 0.0f, 0.0f} + , rotation{0.0f, 0.0f, 0.0f} + , scale{1.0f, 1.0f, 1.0f} + , worldMatrix(MatrixIdentity()) {} +}; + +// Camera3D component - camera settings +struct Camera3DComponent { + Vector3 position; + Vector3 target; + Vector3 up; + float fovy; + int projection; // 0 = CAMERA_PERSPECTIVE, 1 = CAMERA_ORTHOGRAPHIC + + Camera3DComponent() + : position{0.0f, 10.0f, 10.0f} + , target{0.0f, 0.0f, 0.0f} + , up{0.0f, 1.0f, 0.0f} + , fovy(45.0f) + , projection(0) {} +}; + +// Light component - directional light +struct LightComponent { + Vector3 direction; + Color color; + float intensity; + + LightComponent() + : direction{0.5f, 0.7f, 0.3f} + , color(WHITE) + , intensity(1.0f) {} +}; + +// Material component - references shader and material managers +struct MaterialComponent { + unsigned int materialId; // ID in MaterialManager + + MaterialComponent() : materialId(0) {} + MaterialComponent(unsigned int id) : materialId(id) {} +}; + +// RenderPass component - controls rendering order and properties +struct RenderPassComponent { + int order; // Render order (lower = earlier) + bool depthTest; // Enable depth testing + bool depthWrite; // Enable depth writing + unsigned int shaderId; // Override shader for this pass (0 = use material shader) + + RenderPassComponent() + : order(0) + , depthTest(true) + , depthWrite(true) + , shaderId(0) {} +}; diff --git a/include/GuiManager.h b/include/GuiManager.h deleted file mode 100644 index bbd51e0..0000000 --- a/include/GuiManager.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once -#include "rlImGui.h" -#include "imgui.h" -#include "extras/IconsFontAwesome6.h" -#include "gui/fa-solid-900.h" -#include "gui/ImGuiNotify.hpp" -#include "LogWindow.h" // Include the new LogWindow class - -class Application; - -class GuiManager { -public: - GuiManager(); - ~GuiManager(); - - void Initialize(Application* application); - void Render(RenderTexture2D& renderTexture); - void Shutdown(); - -private: - void SetupDockspace(RenderTexture2D& renderTexture); - void RenderNotifications(); - void RenderErrorBanner(); - void SetTheme(); - - bool showLogWindow = true; - Application *app; - LogWindow logWindow; // Add LogWindow as a member -}; \ No newline at end of file diff --git a/include/HotReload.h b/include/HotReload.h index 1802fdb..4545e98 100644 --- a/include/HotReload.h +++ b/include/HotReload.h @@ -1,7 +1,6 @@ #pragma once #include #include -#include class HotReload { public: diff --git a/include/ImViewGuizmoRaymath.h b/include/ImViewGuizmoRaymath.h new file mode 100644 index 0000000..d418e0e --- /dev/null +++ b/include/ImViewGuizmoRaymath.h @@ -0,0 +1,155 @@ +#pragma once + +#include "raylib.h" +#include "raymath.h" +#include "imgui.h" + +#define IMVIEWGUIZMO_VEC3 Vector3 +#define IMVIEWGUIZMO_VEC4 Vector4 +#define IMVIEWGUIZMO_QUAT Quaternion +#define IMVIEWGUIZMO_MAT4 Matrix + +namespace ImViewGuizmo { +namespace GizmoMath { + inline float get_vec_comp(const Vector3& v, int index) { + switch (index) { + case 0: return v.x; + case 1: return v.y; + default: return v.z; + } + } + + inline float get_vec_comp(const Vector4& v, int index) { + switch (index) { + case 0: return v.x; + case 1: return v.y; + case 2: return v.z; + default: return v.w; + } + } + + inline Vector3 make_vec3(float x, float y, float z) { return {x, y, z}; } + inline Vector4 make_vec4(float x, float y, float z, float w) { return {x, y, z, w}; } + + inline Quaternion angleAxis(float angle, const Vector3& axis) { + return QuaternionFromAxisAngle(axis, angle); + } + + inline Quaternion quatLookAt(const Vector3& dir, const Vector3& up) { + Vector3 forward = Vector3Normalize(dir); + Vector3 right = Vector3Normalize(Vector3CrossProduct(up, forward)); + Vector3 correctedUp = Vector3CrossProduct(forward, right); + + Matrix mat = MatrixIdentity(); + mat.m0 = right.x; + mat.m1 = right.y; + mat.m2 = right.z; + mat.m4 = correctedUp.x; + mat.m5 = correctedUp.y; + mat.m6 = correctedUp.z; + mat.m8 = forward.x; + mat.m9 = forward.y; + mat.m10 = forward.z; + return QuaternionFromMatrix(mat); + } + + inline Matrix mat4_identity() { return MatrixIdentity(); } + + inline Vector3 cross(const Vector3& a, const Vector3& b) { + return Vector3CrossProduct(a, b); + } + + inline float dot(const Vector3& a, const Vector3& b) { + return Vector3DotProduct(a, b); + } + + inline float dot(const Quaternion& a, const Quaternion& b) { + return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w; + } + + inline float length(const Vector3& v) { + return Vector3Length(v); + } + + inline float length2(const Vector3& v) { + return v.x * v.x + v.y * v.y + v.z * v.z; + } + + inline Vector3 normalize(const Vector3& v) { + return Vector3Normalize(v); + } + + inline Vector3 mix(const Vector3& a, const Vector3& b, float t) { + return Vector3Lerp(a, b, t); + } + + inline Vector3 add_vv(const Vector3& a, const Vector3& b) { + return Vector3Add(a, b); + } + + inline Vector3 subtract_vv(const Vector3& a, const Vector3& b) { + return Vector3Subtract(a, b); + } + + inline Vector3 multiply_vf(const Vector3& v, float f) { + return Vector3Scale(v, f); + } + + inline Matrix mat4_cast(const Quaternion& q) { + return QuaternionToMatrix(q); + } + + inline Matrix transpose(const Matrix& m) { + return MatrixTranspose(m); + } + + inline Vector3 get_matrix_col(const Matrix& m, int col) { + switch (col) { + case 0: return {m.m0, m.m1, m.m2}; + case 1: return {m.m4, m.m5, m.m6}; + case 2: return {m.m8, m.m9, m.m10}; + default: return {m.m12, m.m13, m.m14}; + } + } + + inline void set_matrix_col(Matrix& m, int col, const Vector4& v) { + switch (col) { + case 0: + m.m0 = v.x; m.m1 = v.y; m.m2 = v.z; m.m3 = v.w; + break; + case 1: + m.m4 = v.x; m.m5 = v.y; m.m6 = v.z; m.m7 = v.w; + break; + case 2: + m.m8 = v.x; m.m9 = v.y; m.m10 = v.z; m.m11 = v.w; + break; + default: + m.m12 = v.x; m.m13 = v.y; m.m14 = v.z; m.m15 = v.w; + break; + } + } + + inline Quaternion multiply_qq(const Quaternion& a, const Quaternion& b) { + return QuaternionMultiply(a, b); + } + + inline Vector3 multiply_qv(const Quaternion& q, const Vector3& v) { + return Vector3RotateByQuaternion(v, q); + } + + inline Matrix multiply_mm(const Matrix& a, const Matrix& b) { + return MatrixMultiply(a, b); + } + + inline Vector4 multiply_mv4(const Matrix& m, const Vector4& v) { + Vector4 result; + result.x = m.m0 * v.x + m.m4 * v.y + m.m8 * v.z + m.m12 * v.w; + result.y = m.m1 * v.x + m.m5 * v.y + m.m9 * v.z + m.m13 * v.w; + result.z = m.m2 * v.x + m.m6 * v.y + m.m10 * v.z + m.m14 * v.w; + result.w = m.m3 * v.x + m.m7 * v.y + m.m11 * v.z + m.m15 * v.w; + return result; + } +} +} // namespace ImViewGuizmo + +#include "ImViewGuizmo.h" diff --git a/include/InputManager.h b/include/InputManager.h new file mode 100644 index 0000000..0b555d7 --- /dev/null +++ b/include/InputManager.h @@ -0,0 +1,82 @@ +#pragma once +#include "raylib.h" +#include +#include +#include + +// Input types supported by the action system +enum class InputType { + Keyboard, + MouseButton, + GamepadButton, + GamepadAxis +}; + +// Represents a single input binding (e.g., KEY_SPACE, MOUSE_BUTTON_LEFT) +struct InputBinding { + InputType type; + int code; // Key code, mouse button, or gamepad button + int gamepadId; // For gamepad inputs (0-3) + float axisThreshold; // For axis inputs (e.g., trigger > 0.5) + bool negative; // For axis inputs (e.g., left stick left = negative) + + InputBinding() + : type(InputType::Keyboard) + , code(0) + , gamepadId(0) + , axisThreshold(0.5f) + , negative(false) {} + + InputBinding(InputType t, int c, int gamepad = 0) + : type(t) + , code(c) + , gamepadId(gamepad) + , axisThreshold(0.5f) + , negative(false) {} +}; + +// Manages input actions with remappable bindings +class InputManager { +public: + InputManager(); + ~InputManager(); + + // Action management + void RegisterAction(const std::string& actionName); + void UnregisterAction(const std::string& actionName); + bool HasAction(const std::string& actionName) const; + + // Binding management + void BindKeyboard(const std::string& actionName, int keyCode); + void BindMouseButton(const std::string& actionName, int mouseButton); + void BindGamepadButton(const std::string& actionName, int gamepadId, int button); + void BindGamepadAxis(const std::string& actionName, int gamepadId, int axis, float threshold, bool negative = false); + void ClearBindings(const std::string& actionName); + void ClearAllActions(); + + // Input queries (called per frame) + bool IsActionPressed(const std::string& actionName) const; + bool IsActionDown(const std::string& actionName) const; + bool IsActionReleased(const std::string& actionName) const; + float GetActionStrength(const std::string& actionName) const; // For analog inputs + + // Configuration + void LoadFromFile(const std::string& filepath); + void SaveToFile(const std::string& filepath) const; + void SetupDefaultBindings(); // Define default controls + + // Get list of all actions + std::vector GetAllActions() const; + + // Get bindings for an action + const std::vector& GetBindings(const std::string& actionName) const; + +private: + std::unordered_map> actions; + + // Helper to check if a single binding is active + bool IsBindingPressed(const InputBinding& binding) const; + bool IsBindingDown(const InputBinding& binding) const; + bool IsBindingReleased(const InputBinding& binding) const; + float GetBindingStrength(const InputBinding& binding) const; +}; diff --git a/include/MaterialManager.h b/include/MaterialManager.h new file mode 100644 index 0000000..fc1b567 --- /dev/null +++ b/include/MaterialManager.h @@ -0,0 +1,44 @@ +#pragma once + +#include "raylib.h" +#include +#include + +struct MaterialData +{ + unsigned int shaderId; + Color albedo; + + MaterialData() : shaderId(0), albedo(WHITE) {} +}; + +class MaterialManager +{ +public: + MaterialManager(); + ~MaterialManager(); + + // Create a material with a shader ID + // Returns a unique ID for the material + unsigned int Create(unsigned int shaderId, Color albedo = WHITE); + + // Remove a material by ID + void Remove(unsigned int id); + + // Get material data by ID (returns nullptr if not found) + MaterialData* Get(unsigned int id); + + // Update material shader + void SetShader(unsigned int id, unsigned int shaderId); + + // Update material albedo + void SetAlbedo(unsigned int id, Color albedo); + + // Clear all materials + void Clear(); + +private: + std::unordered_map materials; + unsigned int nextId; + bool cleared; +}; diff --git a/include/ModelManager.h b/include/ModelManager.h new file mode 100644 index 0000000..759cc5c --- /dev/null +++ b/include/ModelManager.h @@ -0,0 +1,64 @@ +#pragma once + +#include "raylib.h" +#include +#include + +enum class ModelSourceType { + None, + Path, + Cube, + Sphere, + Plane +}; + +struct ModelSource { + ModelSourceType type = ModelSourceType::None; + std::string path; + Vector3 size{1.0f, 1.0f, 1.0f}; + float radius = 0.5f; + int rings = 16; + int slices = 16; +}; + +class ModelManager +{ +public: + ModelManager(); + ~ModelManager(); + + // Load a model from a file + // Returns a unique ID for the model, or 0 on failure + unsigned int Load(const std::string& path); + + // Load a model from a mesh + // Returns a unique ID for the model, or 0 on failure + unsigned int LoadFromMesh(Mesh mesh); + + // Load a model from a generated cube mesh + unsigned int LoadCube(float width, float height, float length); + + // Load a model from a generated sphere mesh + unsigned int LoadSphere(float radius, int rings, int slices); + + // Load a model from a generated plane mesh + unsigned int LoadPlane(float width, float length, int resX, int resZ); + + // Unload a model by ID + void Unload(unsigned int id); + + // Get a model by ID (returns nullptr if not found) + Model* Get(unsigned int id); + + // Get source metadata for a model if available + const ModelSource* GetSource(unsigned int id) const; + + // Unload all models + void UnloadAll(); + +private: + std::unordered_map models; + std::unordered_map modelSources; + unsigned int nextId; + bool unloaded; +}; diff --git a/include/PhysFSManager.h b/include/PhysFSManager.h new file mode 100644 index 0000000..c790bd7 --- /dev/null +++ b/include/PhysFSManager.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +struct PHYSFS_File; + +namespace PhysFSManager { + +bool Initialize(const char* argv0); +bool IsInitialized(); +void Shutdown(); + +bool MountPath(const std::string& path, const std::string& mountPoint, bool append = true); +bool UnmountPath(const std::string& path); +void ClearSearchPath(); + +bool SetWriteDir(const std::string& path); +bool EnsureDir(const std::string& path); + +bool Exists(const std::string& path); +bool IsDirectory(const std::string& path); +std::string GetRealDir(const std::string& path); + +std::int64_t GetModTime(const std::string& path); +std::vector ListFiles(const std::string& path); + +bool ReadTextFile(const std::string& path, std::string& out); +bool WriteTextFile(const std::string& path, const std::string& contents); + +PHYSFS_File* OpenRead(const std::string& path); +PHYSFS_File* OpenWrite(const std::string& path); +void Close(PHYSFS_File* file); + +} // namespace PhysFSManager diff --git a/include/SceneGraph.h b/include/SceneGraph.h new file mode 100644 index 0000000..154febb --- /dev/null +++ b/include/SceneGraph.h @@ -0,0 +1,16 @@ +#pragma once + +#include "entt.hpp" + + +// Attach child to parent (updates linked list) +void AttachChild(entt::registry& registry, entt::entity child, entt::entity parent); + +// Detach child from its parent (if any) +void DetachFromParent(entt::registry& registry, entt::entity child); + +// Detach all children from a parent (orphans them) +void DetachChildren(entt::registry& registry, entt::entity parent); + +// Update WorldTransform for all entities using hierarchy traversal +void UpdateWorldTransforms(entt::registry& registry); diff --git a/include/SceneLoader.h b/include/SceneLoader.h new file mode 100644 index 0000000..b252e99 --- /dev/null +++ b/include/SceneLoader.h @@ -0,0 +1,28 @@ +#pragma once + +#include "entt.hpp" +#include + +class ModelManager; +class ShaderManager; +class TextureManager; +class AssetManager; + + +// Set global context used by scene loading +void SetSceneContext(entt::registry* registry, ModelManager* modelManager, ShaderManager* shaderManager, TextureManager* textureManager); +void SetSceneAssetManager(AssetManager* assetManager); + +// Load a scene from a TOML-like file. Returns the root entity (or entt::null on failure). +entt::entity LoadSceneFromFile(const std::string& path, bool clearExisting); + +// Save the current scene to a TOML-like file. Returns true on success. +bool SaveSceneToFile(const std::string& path); + +// Clear all entities from the registry +void ClearScene(); + +// Scene script path helpers +void SetSceneScriptPath(const std::string& path); +const std::string& GetSceneScriptPath(); +bool ConsumeSceneScriptPath(std::string& outPath); diff --git a/include/ScriptEngine.h b/include/ScriptEngine.h deleted file mode 100644 index a1ec2c6..0000000 --- a/include/ScriptEngine.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -#include "angelscript.h" -#include "scriptbuilder.h" -#include -#include - -class ScriptEngine { -public: - ScriptEngine(); - ~ScriptEngine(); - - bool Initialize(); - void Shutdown(); - bool CompileScript(const std::string& filename); - void CallScriptFunction(asIScriptFunction* func, float dt = 0.0f); - void GarbageCollect(); - - // Add a path used to resolve #include directives in scripts - void AddIncludePath(const std::string& path); - - // Try to preserve script state across hot-reloads by calling optional - // __SerializeState()/__DeserializeState() hooks in the script module. - std::string CaptureScriptState(); - void RestoreScriptState(const std::string &state); - - asIScriptEngine* GetEngine() const { return engine; } - asIScriptFunction* GetUpdateFunction() const { return updateFunc; } - asIScriptFunction* GetDrawFunction() const { return drawFunc; } - -private: - asIScriptEngine* engine; - asIScriptFunction* updateFunc; - asIScriptFunction* drawFunc; - asIScriptModule* currentModule; - bool hasValidScript; - std::vector includePaths; - - static void MessageCallback(const asSMessageInfo* msg, void* param); - static int IncludeCallback(const char* include, const char* from, CScriptBuilder* builder, void* userParam); - std::string ReadFile(const std::string& filename); - void ClearCachedFunctions(); -}; \ No newline at end of file diff --git a/include/ShaderManager.h b/include/ShaderManager.h new file mode 100644 index 0000000..991b40e --- /dev/null +++ b/include/ShaderManager.h @@ -0,0 +1,39 @@ +#pragma once + +#include "raylib.h" +#include +#include + +struct ShaderSource { + std::string vsPath; + std::string fsPath; +}; + +class ShaderManager +{ +public: + ShaderManager(); + ~ShaderManager(); + + // Load a shader from vertex and fragment shader files + // Returns a unique ID for the shader, or 0 on failure + unsigned int Load(const std::string& vsPath, const std::string& fsPath); + + // Unload a shader by ID + void Unload(unsigned int id); + + // Get a shader by ID (returns nullptr if not found) + Shader* Get(unsigned int id); + + // Get source metadata for a shader if available + const ShaderSource* GetSource(unsigned int id) const; + + // Unload all shaders + void UnloadAll(); + +private: + std::unordered_map shaders; + std::unordered_map shaderSources; + unsigned int nextId; + bool unloaded; +}; diff --git a/include/TextureManager.h b/include/TextureManager.h new file mode 100644 index 0000000..e94ac81 --- /dev/null +++ b/include/TextureManager.h @@ -0,0 +1,38 @@ +#pragma once + +#include "raylib.h" +#include +#include + +struct TextureSource { + std::string path; +}; + +class TextureManager +{ +public: + TextureManager(); + ~TextureManager(); + + // Load a texture from a file + // Returns a unique ID for the texture, or 0 on failure + unsigned int Load(const std::string& path); + + // Unload a texture by ID + void Unload(unsigned int id); + + // Get a texture by ID (returns nullptr if not found) + Texture2D* Get(unsigned int id); + + // Get source metadata for a texture if available + const TextureSource* GetSource(unsigned int id) const; + + // Unload all textures + void UnloadAll(); + +private: + std::unordered_map textures; + std::unordered_map textureSources; + unsigned int nextId; + bool unloaded; +}; diff --git a/include/entt.hpp b/include/entt.hpp new file mode 100644 index 0000000..90a121b --- /dev/null +++ b/include/entt.hpp @@ -0,0 +1,95903 @@ +/*! @brief `EnTT` default namespace. */ +namespace entt {} + +// IWYU pragma: begin_exports +// #include "config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "config/macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "config/version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + +// #include "container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator> +class dense_set; + +template +class basic_table; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Element types. + */ +template +using table = basic_table...>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits::max)(); + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + const dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + const dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept = default; + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return (offset = it[static_cast(offset)].next), *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + const dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + const auto idx = static_cast(offset); + return {it[idx].element.first, it[idx].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_map_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_map() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses a given element with bounds checking. + * @tparam Other Type of the key of an element to find. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/bit.hpp" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_set_placeholder_position = (std::numeric_limits::max)(); + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + const dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + const dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept = default; + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[static_cast(offset)].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + const dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[static_cast(offset)].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_set_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_set_placeholder_position; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) { + if(packed.second()(packed.first()[offset].second, value)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) { + if(packed.second()(packed.first()[offset].second, value)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_set() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array 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 noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +#endif + +// #include "container/table.hpp" +#ifndef ENTT_CONTAINER_TABLE_HPP +#define ENTT_CONTAINER_TABLE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class table_iterator { + template + friend class table_iterator; + +public: + using value_type = decltype(std::forward_as_tuple(*std::declval()...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr table_iterator() noexcept + : it{} {} + + constexpr table_iterator(It... from) noexcept + : it{from...} {} + + template && ...)>> + constexpr table_iterator(const table_iterator &other) noexcept + : table_iterator{std::get(other.it)...} {} + + constexpr table_iterator &operator++() noexcept { + return (++std::get(it), ...), *this; + } + + constexpr table_iterator operator++(int) noexcept { + const table_iterator orig = *this; + return ++(*this), orig; + } + + constexpr table_iterator &operator--() noexcept { + return (--std::get(it), ...), *this; + } + + constexpr table_iterator operator--(int) noexcept { + const table_iterator orig = *this; + return operator--(), orig; + } + + constexpr table_iterator &operator+=(const difference_type value) noexcept { + return ((std::get(it) += value), ...), *this; + } + + constexpr table_iterator operator+(const difference_type value) const noexcept { + table_iterator copy = *this; + return (copy += value); + } + + constexpr table_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr table_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return std::forward_as_tuple(std::get(it)[value]...); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return {operator[](0)}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const table_iterator &, const table_iterator &) noexcept; + + template + friend constexpr bool operator==(const table_iterator &, const table_iterator &) noexcept; + + template + friend constexpr bool operator<(const table_iterator &, const table_iterator &) noexcept; + +private: + std::tuple it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const table_iterator &lhs, const table_iterator &rhs) noexcept { + return std::get<0>(lhs.it) - std::get<0>(rhs.it); +} + +template +[[nodiscard]] constexpr bool operator==(const table_iterator &lhs, const table_iterator &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template +[[nodiscard]] constexpr bool operator!=(const table_iterator &lhs, const table_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const table_iterator &lhs, const table_iterator &rhs) noexcept { + return std::get<0>(lhs.it) < std::get<0>(rhs.it); +} + +template +[[nodiscard]] constexpr bool operator>(const table_iterator &lhs, const table_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const table_iterator &lhs, const table_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const table_iterator &lhs, const table_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic table implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a table. Do not make assumption on the order in any case. + * + * @tparam Container Sequence container row types. + */ +template +class basic_table { + using container_type = std::tuple; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Input iterator type. */ + using iterator = internal::table_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::table_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = internal::table_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = internal::table_iterator; + + /*! @brief Default constructor. */ + basic_table() + : payload{} { + } + + /** + * @brief Copy constructs the underlying containers. + * @param container The containers to copy from. + */ + explicit basic_table(const Container &...container) noexcept + : payload{container...} { + ENTT_ASSERT((((std::get(payload).size() * sizeof...(Container)) == (std::get(payload).size() + ...)) && ...), "Unexpected container size"); + } + + /** + * @brief Move constructs the underlying containers. + * @param container The containers to move from. + */ + explicit basic_table(Container &&...container) noexcept + : payload{std::move(container)...} { + ENTT_ASSERT((((std::get(payload).size() * sizeof...(Container)) == (std::get(payload).size() + ...)) && ...), "Unexpected container size"); + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_table(const basic_table &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_table(basic_table &&other) noexcept + : payload{std::move(other.payload)} {} + + /** + * @brief Constructs the underlying containers using a given allocator. + * @tparam Allocator Type of allocator. + * @param allocator A valid allocator. + */ + template + explicit basic_table(const Allocator &allocator) + : payload{Container{allocator}...} {} + + /** + * @brief Copy constructs the underlying containers using a given allocator. + * @tparam Allocator Type of allocator. + * @param container The containers to copy from. + * @param allocator A valid allocator. + */ + template + basic_table(const Container &...container, const Allocator &allocator) noexcept + : payload{Container{container, allocator}...} { + ENTT_ASSERT((((std::get(payload).size() * sizeof...(Container)) == (std::get(payload).size() + ...)) && ...), "Unexpected container size"); + } + + /** + * @brief Move constructs the underlying containers using a given allocator. + * @tparam Allocator Type of allocator. + * @param container The containers to move from. + * @param allocator A valid allocator. + */ + template + basic_table(Container &&...container, const Allocator &allocator) noexcept + : payload{Container{std::move(container), allocator}...} { + ENTT_ASSERT((((std::get(payload).size() * sizeof...(Container)) == (std::get(payload).size() + ...)) && ...), "Unexpected container size"); + } + + /** + * @brief Allocator-extended move constructor. + * @tparam Allocator Type of allocator. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + template + basic_table(basic_table &&other, const Allocator &allocator) + : payload{Container{std::move(std::get(other.payload)), allocator}...} {} + + /*! @brief Default destructor. */ + ~basic_table() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This container. + */ + basic_table &operator=(const basic_table &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + basic_table &operator=(basic_table &&other) noexcept { + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given table. + * @param other Table to exchange the content with. + */ + void swap(basic_table &other) noexcept { + using std::swap; + swap(payload, other.payload); + } + + /** + * @brief Increases the capacity of a table. + * + * 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) { + (std::get(payload).reserve(cap), ...); + } + + /** + * @brief Returns the number of rows that a table has currently allocated + * space for. + * @return Capacity of the table. + */ + [[nodiscard]] size_type capacity() const noexcept { + return std::get<0>(payload).capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + (std::get(payload).shrink_to_fit(), ...); + } + + /** + * @brief Returns the number of rows in a table. + * @return Number of rows. + */ + [[nodiscard]] size_type size() const noexcept { + return std::get<0>(payload).size(); + } + + /** + * @brief Checks whether a table is empty. + * @return True if the table is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return std::get<0>(payload).empty(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the table is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first row of the table. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return {std::get(payload).cbegin()...}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return {std::get(payload).begin()...}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last row of the table. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return {std::get(payload).cend()...}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return {std::get(payload).end()...}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the table is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first row of the reversed table. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return {std::get(payload).crbegin()...}; + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return {std::get(payload).rbegin()...}; + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last row of the reversed + * table. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return {std::get(payload).crend()...}; + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return {std::get(payload).rend()...}; + } + + /** + * @brief Appends a row to the end of a table. + * @tparam Args Types of arguments to use to construct the row data. + * @param args Parameters to use to construct the row data. + * @return A reference to the newly created row data. + */ + template + std::tuple emplace(Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return std::forward_as_tuple(std::get(payload).emplace_back()...); + } else { + return std::forward_as_tuple(std::get(payload).emplace_back(std::forward(args))...); + } + } + + /** + * @brief Removes a row from a table. + * @param pos An iterator to the row to remove. + * @return An iterator following the removed row. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - begin(); + return {std::get(payload).erase(std::get(payload).begin() + diff)...}; + } + + /** + * @brief Removes a row from a table. + * @param pos Index of the row to remove. + */ + void erase(const size_type pos) { + ENTT_ASSERT(pos < size(), "Index out of bounds"); + erase(begin() + static_cast(pos)); + } + + /** + * @brief Returns the row data at specified location. + * @param pos The row for which to return the data. + * @return The row data at specified location. + */ + [[nodiscard]] std::tuple operator[](const size_type pos) const { + ENTT_ASSERT(pos < size(), "Index out of bounds"); + return std::forward_as_tuple(std::get(payload)[pos]...); + } + + /*! @copydoc operator[] */ + [[nodiscard]] std::tuple operator[](const size_type pos) { + ENTT_ASSERT(pos < size(), "Index out of bounds"); + return std::forward_as_tuple(std::get(payload)[pos]...); + } + + /*! @brief Clears a table. */ + void clear() { + (std::get(payload).clear(), ...); + } + +private: + container_type payload; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::bool_constant<(std::uses_allocator_v && ...)> {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #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 + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } +}; + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first + 1; it < last; ++it) { + auto value = std::move(*it); + auto pre = it; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; pre > first && compare(value, *(pre - 1)); --pre) { + *pre = std::move(*(pre - 1)); + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + *pre = std::move(value); + } + } + } +}; + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template +struct radix_sort { + static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ + template + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + constexpr auto passes = N / Bit; + + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + std::vector aux(static_cast(std::distance(first, last))); + + auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { + constexpr auto mask = (1 << Bit) - 1; + constexpr auto buckets = 1 << Bit; + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays, misc-const-correctness) + std::size_t count[buckets]{}; + + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + std::size_t index[buckets]{}; + + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } + + for(auto it = from; it != to; ++it) { + const auto pos = index[(getter(*it) >> start) & mask]++; + out[static_cast(pos)] = std::move(*it); + } + }; + + for(std::size_t pass = 0; pass < (passes & ~1u); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } + + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } + } +}; + +} // namespace entt + +#endif + +// #include "core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct fnv_1a_params; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 2166136261; + static constexpr auto prime = 16777619; +}; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 14695981039346656037ull; + static constexpr auto prime = 1099511628211ull; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr{}; + hash_type hash{fnv_1a_params<>::offset}; + size_type length{}; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using params = internal::fnv_1a_params<>; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const typename base_type::value_type *str) noexcept + : repr{str} {} + + const typename base_type::value_type *repr; + }; + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + [[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : basic_hashed_string{nullptr, 0u} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; base_type::length < len; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + for(; str[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + } + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{wrapper.repr} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; wrapper.repr[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(wrapper.repr[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Returns the size of a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] explicit constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_string operator""_hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_wstring operator""_hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr const char *pretty_function() noexcept { +#if defined ENTT_PRETTY_FUNCTION + return static_cast(ENTT_PRETTY_FUNCTION); +#else + return ""; +#endif +} + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + const std::string_view full_name{pretty_function()}; + auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static const type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "utility.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_request : std::uint8_t { + info, + transfer, + assign, + compare, + copy, + move +}; + +template +struct basic_any_storage { + static constexpr bool has_buffer = true; + union { + const void *instance{}; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + alignas(Align) std::byte buffer[Len]; + }; +}; + +template +struct basic_any_storage<0u, Align> { + static constexpr bool has_buffer = false; + const void *instance{}; +}; + +template +// NOLINTNEXTLINE(bugprone-sizeof-expression) +struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v> {}; + +template +struct in_situ: std::false_type {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any: private internal::basic_any_storage { + using request = internal::any_request; + using base_type = internal::basic_any_storage; + using vtable_type = const void *(const request, const basic_any &, const void *); + using deleter_type = void(const basic_any &); + + template + static constexpr bool in_situ_v = internal::in_situ::value; + + template + static const void *basic_vtable(const request req, const basic_any &value, const void *other) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + + switch(const auto *elem = static_cast(value.data()); req) { + case request::info: + return &type_id(); + case request::transfer: + if constexpr(std::is_move_assignable_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + *const_cast(elem) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case request::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(elem) = *static_cast(other); + return other; + } + break; + case request::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return (*elem == *static_cast(other)) ? other : nullptr; + } else { + return (elem == other) ? other : nullptr; + } + case request::copy: + if constexpr(std::is_copy_constructible_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + static_cast(const_cast(other))->initialize(*elem); + } + break; + case request::move: + ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy"); + if constexpr(in_situ_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion) + return ::new(&static_cast(const_cast(other))->buffer) Type{std::move(*const_cast(elem))}; + } + } + + return nullptr; + } + + template + static void basic_deleter(const basic_any &value) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !std::is_trivially_destructible_v), "Unexpected policy"); + + const auto *elem = static_cast(value.data()); + + if constexpr(in_situ_v) { + (value.mode == any_policy::embedded) ? elem->~Type() : (delete elem); + } else if constexpr(std::is_array_v) { + delete[] elem; + } else { + delete elem; + } + } + + template + void initialize([[maybe_unused]] Args &&...args) { + using plain_type = std::remove_const_t>; + + vtable = basic_vtable; + underlying_type = type_hash::value(); + + if constexpr(std::is_void_v) { + deleter = nullptr; + mode = any_policy::empty; + this->instance = nullptr; + } else if constexpr(std::is_lvalue_reference_v) { + deleter = nullptr; + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) + this->instance = (std::addressof(args), ...); + } else if constexpr(in_situ_v) { + if constexpr(std::is_trivially_destructible_v) { + deleter = nullptr; + } else { + deleter = &basic_deleter; + } + + mode = any_policy::embedded; + + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + ::new(&this->buffer) plain_type{std::forward(args)...}; + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + ::new(&this->buffer) plain_type(std::forward(args)...); + } + } else { + deleter = &basic_deleter; + mode = any_policy::dynamic; + + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + this->instance = new plain_type{std::forward(args)...}; + } else if constexpr(std::is_array_v) { + static_assert(sizeof...(Args) == 0u, "Invalid arguments"); + this->instance = new plain_type[std::extent_v](); + } else { + this->instance = new plain_type(std::forward(args)...); + } + } + } + + void invoke_deleter_if_exists() { + if(deleter != nullptr) { + deleter(*this); + } + } + +public: + /*! @brief Size of the internal buffer. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : base_type{} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value A pointer to an object to take ownership of. + */ + template + explicit basic_any(std::in_place_t, Type *value) + : base_type{} { + static_assert(!std::is_const_v && !std::is_void_v, "Non-const non-void pointer required"); + + if(value == nullptr) { + initialize(); + } else { + initialize(*value); + deleter = &basic_deleter; + mode = any_policy::dynamic; + } + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, 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{} { + other.vtable(request::copy, other, this); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : base_type{}, + vtable{other.vtable}, + deleter{other.deleter}, + underlying_type{other.underlying_type}, + mode{other.mode} { + if(other.mode == any_policy::embedded) { + other.vtable(request::move, other, this); + } else if(other.mode != any_policy::empty) { + this->instance = std::exchange(other.instance, nullptr); + } + } + + /*! @brief Frees the internal buffer, whatever it means. */ + ~basic_any() { + invoke_deleter_if_exists(); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + if(this != &other) { + invoke_deleter_if_exists(); + + if(other) { + other.vtable(request::copy, other, this); + } else { + initialize(); + } + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + if(this != &other) { + invoke_deleter_if_exists(); + + if(other.mode == any_policy::embedded) { + other.vtable(request::move, other, this); + } else if(other.mode != any_policy::empty) { + this->instance = std::exchange(other.instance, nullptr); + } + + vtable = other.vtable; + deleter = other.deleter; + underlying_type = other.underlying_type; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template, basic_any>>> + basic_any &operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] bool has_value() const noexcept { + return (mode != any_policy::empty); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @param req Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + [[nodiscard]] bool has_value(const type_info &req) const noexcept { + return (underlying_type == req.hash()); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @tparam Type Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + template + [[nodiscard]] bool has_value() const noexcept { + static_assert(std::is_same_v, Type>, "Invalid type"); + return (underlying_type == type_hash::value()); + } + + /** + * @brief Returns the object type info if any, `type_id()` otherwise. + * @return The object type info if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *static_cast(vtable(request::info, *this, nullptr)); + } + + /*! @copydoc info */ + [[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept { + return info(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + if constexpr(base_type::has_buffer) { + return (mode == any_policy::embedded) ? &this->buffer : this->instance; + } else { + return this->instance; + } + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return has_value(req) ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @tparam Type Expected type. + * @return An opaque pointer the contained instance, if any. + */ + template + [[nodiscard]] const Type *data() const noexcept { + return has_value>() ? static_cast(data()) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @tparam Type Expected type. + * @return An opaque pointer the contained instance, if any. + */ + template + [[nodiscard]] Type *data() noexcept { + if constexpr(std::is_const_v) { + return std::as_const(*this).template data>(); + } else { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).template data>()); + } + } + + /** + * @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) { + invoke_deleter_if_exists(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) { + return (vtable(request::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + bool assign(basic_any &&other) { + if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) { + return (other.mode == any_policy::cref) ? (vtable(request::assign, *this, std::as_const(other).data()) != nullptr) : (vtable(request::transfer, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @brief Destroys contained object */ + void reset() { + invoke_deleter_if_exists(); + initialize(); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return has_value(); + } + + /** + * @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 basic_any &other) const noexcept { + if(other && (underlying_type == other.underlying_type)) { + return (vtable(request::compare, *this, other.data()) != nullptr); + } + + return (!*this && !other); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + basic_any other = std::as_const(*this).as_ref(); + other.mode = (mode == any_policy::cref ? any_policy::cref : any_policy::ref); + return other; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + basic_any other{}; + other.instance = data(); + other.vtable = vtable; + other.underlying_type = underlying_type; + other.mode = any_policy::cref; + return other; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::dynamic || mode == any_policy::embedded); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + vtable_type *vtable{}; + deleter_type *deleter{}; + id_type underlying_type{}; + any_policy mode{}; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] std::remove_const_t any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] std::remove_const_t any_cast(basic_any &data) 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, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) +[[nodiscard]] std::remove_const_t any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } + + return any_cast(data); + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + return data->template data>(); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + return data->template data(); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "core/enum.hpp" +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include + +namespace entt { + +/** + * @brief Enable bitmask support for enum classes. + * @tparam Type The enum type for which to enable bitmask support. + */ +template +struct enum_as_bitmask: std::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template +struct enum_as_bitmask>: std::is_enum {}; + +/** + * @brief Helper variable template. + * @tparam Type The enum class type for which to enable bitmask support. + */ +template +inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; + +} // namespace entt + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param lhs The first value to use. + * @param rhs The second value to use. + * @return The result of invoking the operator on the underlying types of the + * two values provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator|(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator&(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator^(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param value The value to use. + * @return The result of invoking the operator on the underlying types of the + * value provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator~(const Type value) noexcept { + return static_cast(~static_cast>(value)); +} + +/*! @copydoc operator~ */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> +operator!(const Type value) noexcept { + return !static_cast>(value); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator|=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator&=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator^=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs ^ rhs)); +} + +#endif + +// #include "core/family.hpp" +#ifndef ENTT_CORE_FAMILY_HPP +#define ENTT_CORE_FAMILY_HPP + +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Dynamic identifier generator. + * + * Utility class template that can be used to assign unique identifiers to types + * at runtime. Use different specializations to create separate sets of + * identifiers. + */ +template +class family { + static auto identifier() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } + +public: + /*! @brief Unsigned integer type. */ + using value_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type + inline static const value_type value = identifier(); +}; + +} // namespace entt + +#endif + +// #include "core/hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct fnv_1a_params; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 2166136261; + static constexpr auto prime = 16777619; +}; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 14695981039346656037ull; + static constexpr auto prime = 1099511628211ull; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr{}; + hash_type hash{fnv_1a_params<>::offset}; + size_type length{}; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using params = internal::fnv_1a_params<>; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const typename base_type::value_type *str) noexcept + : repr{str} {} + + const typename base_type::value_type *repr; + }; + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + [[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : basic_hashed_string{nullptr, 0u} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; base_type::length < len; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + for(; str[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + } + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{wrapper.repr} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; wrapper.repr[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(wrapper.repr[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Returns the size of a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] explicit constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_string operator""_hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_wstring operator""_hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + +// #include "core/ident.hpp" +#ifndef ENTT_CORE_IDENT_HPP +#define ENTT_CORE_IDENT_HPP + +#include +#include +#include +// #include "fwd.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @brief Type integral identifiers. + * @tparam Type List of types for which to generate identifiers. + */ +template +class ident { + template + [[nodiscard]] static constexpr id_type get(std::index_sequence) noexcept { + static_assert((std::is_same_v || ...), "Invalid type"); + return (0 + ... + (std::is_same_v...>>> ? id_type{Index} : id_type{})); + } + +public: + /*! @brief Unsigned integer type. */ + using value_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + static constexpr value_type value = get>(std::index_sequence_for{}); +}; + +} // namespace entt + +#endif + +// #include "core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "core/monostate.hpp" +#ifndef ENTT_CORE_MONOSTATE_HPP +#define ENTT_CORE_MONOSTATE_HPP + +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Minimal implementation of the monostate pattern. + * + * A minimal, yet complete configuration system built on top of the monostate + * pattern. Thread safe by design, it works only with basic types like `int`s or + * `bool`s.
+ * Multiple types and therefore more than one value can be associated with a + * single key. Because of this, users must pay attention to use the same type + * both during an assignment and when they try to read back their data. + * Otherwise, they can incur in unexpected results. + */ +template +struct monostate { + /** + * @brief Assigns a value of a specific type to a given key. + * @tparam Type Type of the value to assign. + * @param val User data to assign to the given key. + * @return This monostate object. + */ + template + monostate &operator=(Type val) noexcept { + value = val; + return *this; + } + + /** + * @brief Gets a value of a specific type for a given key. + * @tparam Type Type of the value to get. + * @return Stored value, if any. + */ + template + operator Type() const noexcept { + return value; + } + +private: + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + inline static ENTT_MAYBE_ATOMIC(Type) value{}; +}; + +/** + * @brief Helper variable template. + * @tparam Value Value used to differentiate between different variables. + */ +template +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +inline monostate monostate_v{}; + +} // namespace entt + +#endif + +// #include "core/ranges.hpp" +#ifndef ENTT_CORE_RANGES_HPP +#define ENTT_CORE_RANGES_HPP + +#if __has_include() +# include +# +# if defined(__cpp_lib_ranges) +# include +// # include "iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + + +template +inline constexpr bool std::ranges::enable_borrowed_range>{true}; + +template +inline constexpr bool std::ranges::enable_view>{true}; + +# endif +#endif + +#endif +// #include "core/tuple.hpp" +#ifndef ENTT_CORE_TUPLE_HPP +#define ENTT_CORE_TUPLE_HPP + +#include +#include +#include + +namespace entt { + +/** + * @brief Provides the member constant `value` to true if a given type is a + * tuple, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_tuple: std::false_type {}; + +/** + * @copybrief is_tuple + * @tparam Args Tuple template arguments. + */ +template +struct is_tuple>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_tuple_v = is_tuple::value; + +/** + * @brief Utility function to unwrap tuples of a single element. + * @tparam Type Tuple type of any sizes. + * @param value A tuple object of the given type. + * @return The tuple itself if it contains more than one element, the first + * element otherwise. + */ +template +constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept { + if constexpr(std::tuple_size_v> == 1u) { + return std::get<0>(std::forward(value)); + } else { + return std::forward(value); + } +} + +/** + * @brief Utility class to forward-and-apply tuple objects. + * @tparam Func Type of underlying invocable object. + */ +template +struct forward_apply: private Func { + /** + * @brief Constructs a forward-and-apply object. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v) + : Func{std::forward(args)...} {} + + /** + * @brief Forwards and applies the arguments with the underlying function. + * @tparam Type Tuple-like type to forward to the underlying function. + * @param args Parameters to forward to the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval(), args))) { + return std::apply(static_cast(*this), std::forward(args)); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval(), args))) { + return std::apply(static_cast(*this), std::forward(args)); + } +}; + +/** + * @brief Deduction guide. + * @tparam Func Type of underlying invocable object. + */ +template +forward_apply(Func) -> forward_apply>>; + +} // namespace entt + +#endif + +// #include "core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr const char *pretty_function() noexcept { +#if defined ENTT_PRETTY_FUNCTION + return static_cast(ENTT_PRETTY_FUNCTION); +#else + return ""; +#endif +} + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + const std::string_view full_name{pretty_function()}; + auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static const type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "entity/component.hpp" +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_ENTITY_FWD_HPP +#define ENTT_ENTITY_FWD_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @brief Default entity identifier. */ +enum class entity : id_type {}; + +/*! @brief Storage deletion policy. */ +enum class deletion_policy : std::uint8_t { + /*! @brief Swap-and-pop deletion policy. */ + swap_and_pop = 0u, + /*! @brief In-place deletion policy. */ + in_place = 1u, + /*! @brief Swap-only deletion policy. */ + swap_only = 2u, + /*! @brief Unspecified deletion policy. */ + unspecified = swap_and_pop +}; + +template +struct component_traits; + +template> +class basic_sparse_set; + +template, typename = void> +class basic_storage; + +template +class basic_sigh_mixin; + +template +class basic_reactive_mixin; + +template> +class basic_registry; + +template +class basic_view; + +template> +class basic_runtime_view; + +template +class basic_group; + +template +class basic_organizer; + +template +class basic_handle; + +template +class basic_snapshot; + +template +class basic_snapshot_loader; + +template +class basic_continuous_loader; + +/*! @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 Type Element type. + */ +template +using storage = basic_storage; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Underlying storage type. + */ +template +using sigh_mixin = basic_sigh_mixin>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Underlying storage type. + */ +template +using reactive_mixin = basic_reactive_mixin>; + +/*! @brief Alias declaration for the most common use case. */ +using registry = basic_registry<>; + +/*! @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. */ +using runtime_view = basic_runtime_view; + +/*! @brief Alias declaration for the most common use case. */ +using const_runtime_view = basic_runtime_view; + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template +struct exclude_t final: type_list { + /*! @brief Default constructor. */ + explicit constexpr exclude_t() = default; +}; + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template +inline constexpr exclude_t exclude{}; + +/** + * @brief Alias for lists of observed elements. + * @tparam Type List of types. + */ +template +struct get_t final: type_list { + /*! @brief Default constructor. */ + explicit constexpr get_t() = default; +}; + +/** + * @brief Variable template for lists of observed elements. + * @tparam Type List of types. + */ +template +inline constexpr get_t get{}; + +/** + * @brief Alias for lists of owned elements. + * @tparam Type List of types. + */ +template +struct owned_t final: type_list { + /*! @brief Default constructor. */ + explicit constexpr owned_t() = default; +}; + +/** + * @brief Variable template for lists of owned elements. + * @tparam Type List of types. + */ +template +inline constexpr owned_t owned{}; + +/** + * @brief Applies a given _function_ to a get list and generate a new list. + * @tparam Type Types provided by the get list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting get list after applying the transform function. */ + using type = get_t::type...>; +}; + +/** + * @brief Applies a given _function_ to an exclude list and generate a new list. + * @tparam Type Types provided by the exclude list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting exclude list after applying the transform function. */ + using type = exclude_t::type...>; +}; + +/** + * @brief Applies a given _function_ to an owned list and generate a new list. + * @tparam Type Types provided by the owned list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting owned list after applying the transform function. */ + using type = owned_t::type...>; +}; + +/** + * @brief Provides a common way to define storage types. + * @tparam Type Storage value type. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template, typename = void> +struct storage_type { + /*! @brief Type-to-storage conversion result. */ + using type = ENTT_STORAGE(sigh_mixin, basic_storage); +}; + +/*! @brief Empty value type for reactive storage types. */ +struct reactive final {}; + +/** + * @ brief Partial specialization for reactive storage types. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct storage_type { + /*! @brief Type-to-storage conversion result. */ + using type = ENTT_STORAGE(reactive_mixin, basic_storage); +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_type_t = typename storage_type::type; + +/** + * Type-to-storage conversion utility that preserves constness. + * @tparam Type Storage value type, eventually const. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template>> +struct storage_for { + /*! @brief Type-to-storage conversion result. */ + using type = constness_as_t, Entity, Allocator>, Type>; +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_for_t = typename storage_for::type; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template> +using view = basic_view, type_list_transform_t>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template, typename Exclude = exclude_t<>> +using group = basic_group, type_list_transform_t, type_list_transform_t>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; + +template<> +struct in_place_delete: std::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; + +template<> +struct page_size: std::integral_constant {}; + +template +struct page_size> + : std::integral_constant {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Element type. + * @tparam Entity A valid entity type. + */ +template +struct component_traits { + static_assert(std::is_same_v, Type>, "Unsupported type"); + + /*! @brief Element type. */ + using element_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ + static constexpr std::size_t page_size = internal::page_size::value; +}; + +} // namespace entt + +#endif + +// #include "entity/entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct entt_traits; + +template +struct entt_traits>> + : entt_traits> { + using value_type = Type; +}; + +template +struct entt_traits>> + : entt_traits { + using value_type = Type; +}; + +template<> +struct entt_traits { + using value_type = std::uint32_t; + + using entity_type = std::uint32_t; + using version_type = std::uint16_t; + + static constexpr entity_type entity_mask = 0xFFFFF; + static constexpr entity_type version_mask = 0xFFF; +}; + +template<> +struct entt_traits { + using value_type = std::uint64_t; + + using entity_type = std::uint64_t; + using version_type = std::uint32_t; + + static constexpr entity_type entity_mask = 0xFFFFFFFF; + static constexpr entity_type version_mask = 0xFFFFFFFF; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. + */ +template +class basic_entt_traits { + static constexpr auto length = popcount(Traits::entity_mask); + + static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask"); + static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask"); + +public: + /*! @brief Value type. */ + using value_type = typename Traits::value_type; + /*! @brief Underlying entity type. */ + using entity_type = typename Traits::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename Traits::version_type; + + /*! @brief Entity mask size. */ + static constexpr entity_type entity_mask = Traits::entity_mask; + /*! @brief Version mask size */ + static constexpr entity_type version_mask = Traits::version_mask; + + /** + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { + return static_cast(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { + return (to_integral(value) & entity_mask); + } + + /** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { + if constexpr(Traits::version_mask == 0u) { + return version_type{}; + } else { + return (static_cast(to_integral(value) >> length) & version_mask); + } + } + + /** + * @brief Returns the successor of a given identifier. + * @param value The identifier of which to return the successor. + * @return The successor of the given identifier. + */ + [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { + const auto vers = to_version(value) + 1; + return construct(to_integral(value), static_cast(vers + (vers == version_mask))); + } + + /** + * @brief Constructs an identifier from its parts. + * + * If the version part is not provided, a tombstone is returned.
+ * If the entity part is not provided, a null identifier is returned. + * + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { + if constexpr(Traits::version_mask == 0u) { + return value_type{entity & entity_mask}; + } else { + return value_type{(entity & entity_mask) | (static_cast(version & version_mask) << length)}; + } + } + + /** + * @brief Combines two identifiers in a single one. + * + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { + if constexpr(Traits::version_mask == 0u) { + return value_type{lhs & entity_mask}; + } else { + return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))}; + } + } +}; + +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template +struct entt_traits: basic_entt_traits> { + /*! @brief Base type. */ + using base_type = basic_entt_traits>; + /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + +/** + * @brief Converts an entity to its underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { + return entt_traits::to_integral(value); +} + +/** + * @brief Returns the entity part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { + return entt_traits::to_entity(value); +} + +/** + * @brief Returns the version part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ +template +[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { + return entt_traits::to_version(value); +} + +/*! @brief Null object for all identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { + return true; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { + return false; + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_entity(entity) == traits_type::to_entity(*this); + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept { + return rhs.operator==(lhs); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept { + return !(rhs == lhs); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { + /** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { + return false; + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + + if constexpr(traits_type::version_mask == 0u) { + return false; + } else { + return (traits_type::to_version(entity) == traits_type::to_version(*this)); + } + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept { + return rhs.operator==(lhs); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept { + return !(rhs == lhs); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + +} // namespace entt + +#endif + +// #include "entity/group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include +#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 + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } +}; + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first + 1; it < last; ++it) { + auto value = std::move(*it); + auto pre = it; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; pre > first && compare(value, *(pre - 1)); --pre) { + *pre = std::move(*(pre - 1)); + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + *pre = std::move(value); + } + } + } +}; + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template +struct radix_sort { + static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ + template + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + constexpr auto passes = N / Bit; + + using value_type = typename std::iterator_traits::value_type; + using difference_type = typename std::iterator_traits::difference_type; + std::vector aux(static_cast(std::distance(first, last))); + + auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { + constexpr auto mask = (1 << Bit) - 1; + constexpr auto buckets = 1 << Bit; + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays, misc-const-correctness) + std::size_t count[buckets]{}; + + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } + + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + std::size_t index[buckets]{}; + + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } + + for(auto it = from; it != to; ++it) { + const auto pos = index[(getter(*it) >> start) & mask]++; + out[static_cast(pos)] = std::move(*it); + } + }; + + for(std::size_t pass = 0; pass < (passes & ~1u); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } + + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } + } +}; + +} // namespace entt + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct fnv_1a_params; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 2166136261; + static constexpr auto prime = 16777619; +}; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 14695981039346656037ull; + static constexpr auto prime = 1099511628211ull; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr{}; + hash_type hash{fnv_1a_params<>::offset}; + size_type length{}; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using params = internal::fnv_1a_params<>; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const typename base_type::value_type *str) noexcept + : repr{str} {} + + const typename base_type::value_type *repr; + }; + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + [[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : basic_hashed_string{nullptr, 0u} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; base_type::length < len; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + for(; str[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + } + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{wrapper.repr} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; wrapper.repr[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(wrapper.repr[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Returns the size of a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] explicit constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_string operator""_hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_wstring operator""_hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr const char *pretty_function() noexcept { +#if defined ENTT_PRETTY_FUNCTION + return static_cast(ENTT_PRETTY_FUNCTION); +#else + return ""; +#endif +} + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + const std::string_view full_name{pretty_function()}; + auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static const type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/bit.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct entt_traits; + +template +struct entt_traits>> + : entt_traits> { + using value_type = Type; +}; + +template +struct entt_traits>> + : entt_traits { + using value_type = Type; +}; + +template<> +struct entt_traits { + using value_type = std::uint32_t; + + using entity_type = std::uint32_t; + using version_type = std::uint16_t; + + static constexpr entity_type entity_mask = 0xFFFFF; + static constexpr entity_type version_mask = 0xFFF; +}; + +template<> +struct entt_traits { + using value_type = std::uint64_t; + + using entity_type = std::uint64_t; + using version_type = std::uint32_t; + + static constexpr entity_type entity_mask = 0xFFFFFFFF; + static constexpr entity_type version_mask = 0xFFFFFFFF; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. + */ +template +class basic_entt_traits { + static constexpr auto length = popcount(Traits::entity_mask); + + static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask"); + static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask"); + +public: + /*! @brief Value type. */ + using value_type = typename Traits::value_type; + /*! @brief Underlying entity type. */ + using entity_type = typename Traits::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename Traits::version_type; + + /*! @brief Entity mask size. */ + static constexpr entity_type entity_mask = Traits::entity_mask; + /*! @brief Version mask size */ + static constexpr entity_type version_mask = Traits::version_mask; + + /** + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { + return static_cast(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { + return (to_integral(value) & entity_mask); + } + + /** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { + if constexpr(Traits::version_mask == 0u) { + return version_type{}; + } else { + return (static_cast(to_integral(value) >> length) & version_mask); + } + } + + /** + * @brief Returns the successor of a given identifier. + * @param value The identifier of which to return the successor. + * @return The successor of the given identifier. + */ + [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { + const auto vers = to_version(value) + 1; + return construct(to_integral(value), static_cast(vers + (vers == version_mask))); + } + + /** + * @brief Constructs an identifier from its parts. + * + * If the version part is not provided, a tombstone is returned.
+ * If the entity part is not provided, a null identifier is returned. + * + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { + if constexpr(Traits::version_mask == 0u) { + return value_type{entity & entity_mask}; + } else { + return value_type{(entity & entity_mask) | (static_cast(version & version_mask) << length)}; + } + } + + /** + * @brief Combines two identifiers in a single one. + * + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { + if constexpr(Traits::version_mask == 0u) { + return value_type{lhs & entity_mask}; + } else { + return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))}; + } + } +}; + +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template +struct entt_traits: basic_entt_traits> { + /*! @brief Base type. */ + using base_type = basic_entt_traits>; + /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + +/** + * @brief Converts an entity to its underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { + return entt_traits::to_integral(value); +} + +/** + * @brief Returns the entity part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { + return entt_traits::to_entity(value); +} + +/** + * @brief Returns the version part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ +template +[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { + return entt_traits::to_version(value); +} + +/*! @brief Null object for all identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { + return true; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { + return false; + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_entity(entity) == traits_type::to_entity(*this); + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity lhs, const null_t rhs) noexcept { + return rhs.operator==(lhs); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity lhs, const null_t rhs) noexcept { + return !(rhs == lhs); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { + /** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { + return false; + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + + if constexpr(traits_type::version_mask == 0u) { + return false; + } else { + return (traits_type::to_version(entity) == traits_type::to_version(*this)); + } + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity lhs, const tombstone_t rhs) noexcept { + return rhs.operator==(lhs); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param lhs Identifier with which to compare. + * @param rhs A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity lhs, const tombstone_t rhs) noexcept { + return !(rhs == lhs); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class extended_group_iterator; + +template +class extended_group_iterator, get_t> { + template + [[nodiscard]] auto index_to_element([[maybe_unused]] Type &cpool) const { + if constexpr(std::is_void_v) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + using iterator_type = It; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_group_iterator() + : it{}, + pools{} {} + + extended_group_iterator(iterator_type from, std::tuple cpools) + : it{from}, + pools{std::move(cpools)} {} + + extended_group_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_group_iterator operator++(int) noexcept { + const extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +struct group_descriptor { + using size_type = std::size_t; + virtual ~group_descriptor() = default; + [[nodiscard]] virtual bool owned(const id_type) const noexcept { + return false; + } +}; + +template +class group_handler final: public group_descriptor { + using entity_type = typename Type::entity_type; + + void swap_elements(const std::size_t pos, const entity_type entt) { + for(size_type next{}; next < Owned; ++next) { + pools[next]->swap_elements((*pools[next])[pos], entt); + } + } + + void push_on_construct(const entity_type entt) { + if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + swap_elements(len++, entt); + } + } + + void remove_if(const entity_type entt) { + if(pools[0u]->contains(entt) && (pools[0u]->index(entt) < len)) { + swap_elements(--len, entt); + } + } + + void common_setup() { + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto first = pools[0u]->rbegin(), last = first + static_cast(pools[0u]->size()); first != last; ++first) { + push_on_construct(*first); + } + } + +public: + using common_type = Type; + using size_type = typename Type::size_type; + + template + group_handler(std::tuple ogpool, std::tuple epool) + : pools{std::apply([](auto &&...cpool) { return std::array{&cpool...}; }, ogpool)}, + filter{std::apply([](auto &&...cpool) { return std::array{&cpool...}; }, epool)} { + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, ogpool); + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool); + common_setup(); + } + + [[nodiscard]] bool owned(const id_type hash) const noexcept override { + for(size_type pos{}; pos < Owned; ++pos) { + if(pools[pos]->info().hash() == hash) { + return true; + } + } + + return false; + } + + [[nodiscard]] size_type length() const noexcept { + return len; + } + + template + [[nodiscard]] common_type *storage() const noexcept { + if constexpr(Index < (Owned + Get)) { + return pools[Index]; + } else { + return filter[Index - (Owned + Get)]; + } + } + +private: + std::array pools; + std::array filter; + std::size_t len{}; +}; + +template +class group_handler final: public group_descriptor { + using entity_type = typename Type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + elem.push(entt); + } + } + + void remove_if(const entity_type entt) { + elem.remove(entt); + } + + void common_setup() { + for(const auto entity: *pools[0u]) { + push_on_construct(entity); + } + } + +public: + using common_type = Type; + + template + group_handler(const Allocator &allocator, std::tuple gpool, std::tuple epool) + : pools{std::apply([](auto &&...cpool) { return std::array{&cpool...}; }, gpool)}, + filter{std::apply([](auto &&...cpool) { return std::array{&cpool...}; }, epool)}, + elem{allocator} { + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, gpool); + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool); + common_setup(); + } + + [[nodiscard]] common_type &handle() noexcept { + return elem; + } + + [[nodiscard]] const common_type &handle() const noexcept { + return elem; + } + + template + [[nodiscard]] common_type *storage() const noexcept { + if constexpr(Index < Get) { + return pools[Index]; + } else { + return filter[Index - Get]; + } + } + +private: + std::array pools; + std::array filter; + common_type elem; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @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 are at + * least in the given storage. Moreover, it's guaranteed that the entity list is + * tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, elements are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + [[nodiscard]] auto pools_for(std::index_sequence) const noexcept { + using return_type = std::tuple; + return descriptor ? return_type{static_cast(descriptor->template storage())...} : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler; + + /** + * @brief Group opaque identifier. + * @return Group opaque identifier. + */ + static id_type group_id() noexcept { + return type_hash, get_t...>, exclude_t...>>>::value(); + } + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return descriptor->handle(); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t>; + return *this ? static_cast(descriptor->template storage()) : nullptr; + } + + /** + * @brief Returns the number of entities that are part of the group. + * @return Number of entities that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? handle().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 noexcept { + return *this ? handle().capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + descriptor->handle().shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || handle().empty(); + } + + /** + * @brief Returns an iterator 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 noexcept { + return *this ? handle().begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? handle().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 noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return *this ? handle().find(entt) : iterator{}; + } + + /** + * @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()[static_cast(pos)]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Type Type of the element to get. + * @tparam Other Other types of elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Index Indexes of the elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools_for(std::index_sequence_for{}); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and elements 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 elements. The + * _constness_ of the elements 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_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty elements. The _constness_ of the + * elements is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools_for(std::index_sequence_for{}); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group 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(std::tuple, std::tuple); + * bool(const Type &..., const Type &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` 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 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 Type Optional type of element to compare. + * @tparam Other Other optional types of elements 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) { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of elements 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...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + descriptor->handle().sort(std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, cpools = pools_for(std::index_sequence_for{})](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + descriptor->handle().sort(std::move(comp), std::move(algo), std::forward(args)...); + } + } + } + + /** + * @brief Sort entities according to their order in a range. + * + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. + * + * @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 sort_as(It first, It last) const { + if(*this) { + descriptor->handle().sort_as(first, last); + } + } + +private: + handler *descriptor; +}; + +/** + * @brief Owning group. + * + * Owning groups returns all entities and only the entities that are at + * least in the given storage. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that all elements in the owned storage are tightly packed + * in memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned storage and all instances have the + * same order in memory. + * + * The more types of storage are owned, the faster it is to iterate a group. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, elements are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + static_assert(((Owned::storage_policy != deletion_policy::in_place) && ...), "Groups do not support in-place delete"); + + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + [[nodiscard]] auto pools_for(std::index_sequence, std::index_sequence) const noexcept { + using return_type = std::tuple; + return descriptor ? return_type{static_cast(descriptor->template storage())..., static_cast(descriptor->template storage())...} : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler; + + /** + * @brief Group opaque identifier. + * @return Group opaque identifier. + */ + static id_type group_id() noexcept { + return type_hash...>, get_t...>, exclude_t...>>>::value(); + } + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return *storage<0>(); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t>; + return *this ? static_cast(descriptor->template storage()) : nullptr; + } + + /** + * @brief Returns the number of entities that that are part of the group. + * @return Number of entities that that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? descriptor->length() : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || !descriptor->length(); + } + + /** + * @brief Returns an iterator 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 noexcept { + return *this ? (handle().end() - static_cast(descriptor->length())) : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? (handle().rbegin() + static_cast(descriptor->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 noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + const auto it = *this ? handle().find(entt) : iterator{}; + return it >= begin() ? it : iterator{}; + } + + /** + * @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()[static_cast(pos)]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Type Type of the element to get. + * @tparam Other Other types of elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Index Indexes of the elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools_for(std::index_sequence_for{}, std::index_sequence_for{}); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and elements 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 elements. The + * _constness_ of the elements 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_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty elements. The _constness_ of the + * elements is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools_for(std::index_sequence_for{}, std::index_sequence_for{}); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group 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(std::tuple, std::tuple); + * bool(const Type &, const Type &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` 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 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 Type Optional type of element to compare. + * @tparam Other Other optional types of elements 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 { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of elements 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 { + const auto cpools = pools_for(std::index_sequence_for{}, std::index_sequence_for{}); + + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward(args)...); + } + + auto cb = [this](auto *head, auto *...other) { + for(auto next = descriptor->length(); next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }; + + std::apply(cb, cpools); + } + +private: + handler *descriptor; +}; + +} // namespace entt + +#endif + +// #include "entity/handle.hpp" +#ifndef ENTT_ENTITY_HANDLE_HPP +#define ENTT_ENTITY_HANDLE_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class handle_storage_iterator final { + template + friend class handle_storage_iterator; + + using underlying_type = std::remove_reference_t; + using entity_type = typename underlying_type::entity_type; + +public: + using value_type = typename std::iterator_traits::value_type; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr handle_storage_iterator() noexcept + : entt{null}, + it{}, + last{} {} + + constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept + : entt{value}, + it{from}, + last{to} { + while(it != last && !it->second.contains(entt)) { + ++it; + } + } + + constexpr handle_storage_iterator &operator++() noexcept { + for(++it; it != last && !it->second.contains(entt); ++it) {} + return *this; + } + + constexpr handle_storage_iterator operator++(int) noexcept { + const handle_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *it; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr bool operator==(const handle_storage_iterator &, const handle_storage_iterator &) noexcept; + +private: + entity_type entt; + It it; + It last; +}; + +template +[[nodiscard]] constexpr bool operator==(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Non-owning handle to an entity. + * + * Tiny wrapper around a registry and an entity. + * + * @tparam Registry Basic registry type. + * @tparam Scope Types to which to restrict the scope of a handle. + */ +template +class basic_handle { + using traits_type = entt_traits; + + [[nodiscard]] auto &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + +public: + /*! @brief Type of registry accepted by the handle. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable handle type. */ + using iterable = iterable_adaptor().storage())::iterator>>; + + /*! @brief Constructs an invalid handle. */ + basic_handle() noexcept + : owner{}, + entt{null} {} + + /** + * @brief Constructs a handle from a given registry and entity. + * @param ref An instance of the registry class. + * @param value A valid identifier. + */ + basic_handle(registry_type &ref, entity_type value) noexcept + : owner{&ref}, + entt{value} {} + + /** + * @brief Returns an iterable object to use to _visit_ a handle. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage.
+ * Returned storage are those that contain the entity associated with the + * handle. + * + * @return An iterable object to use to _visit_ the handle. + */ + [[nodiscard]] iterable storage() const noexcept { + auto underlying = owner_or_assert().storage(); + return iterable{{entt, underlying.begin(), underlying.end()}, {entt, underlying.end(), underlying.end()}}; + } + + /*! @copydoc valid */ + [[nodiscard]] explicit operator bool() const noexcept { + return owner && owner->valid(entt); + } + + /** + * @brief Checks if a handle refers to a valid registry and entity. + * @return True if the handle refers to a valid registry and entity, false + * otherwise. + */ + [[nodiscard]] bool valid() const { + return static_cast(*this); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] registry_type *registry() const noexcept { + return owner; + } + + /** + * @brief Returns the entity associated with a handle. + * @return The entity associated with the handle. + */ + [[nodiscard]] entity_type entity() const noexcept { + return entt; + } + + /*! @copydoc entity */ + [[nodiscard]] operator entity_type() const noexcept { + return entity(); + } + + /*! @brief Destroys the entity associated with a handle. */ + void destroy() { + owner_or_assert().destroy(std::exchange(entt, null)); + } + + /** + * @brief Destroys the entity associated with a handle. + * @param version A desired version upon destruction. + */ + void destroy(const version_type version) { + owner_or_assert().destroy(std::exchange(entt, null), version); + } + + /** + * @brief Assigns the given element to a handle. + * @tparam Type Type of element to create. + * @tparam Args Types of arguments to use to construct the element. + * @param args Parameters to use to initialize the element. + * @return A reference to the newly created element. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + decltype(auto) emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return owner_or_assert().template emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns or replaces the given element for a handle. + * @tparam Type Type of element to assign or replace. + * @tparam Args Types of arguments to use to construct the element. + * @param args Parameters to use to initialize the element. + * @return A reference to the newly created element. + */ + template + decltype(auto) emplace_or_replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return owner_or_assert().template emplace_or_replace(entt, std::forward(args)...); + } + + /** + * @brief Patches the given element for a handle. + * @tparam Type Type of element to patch. + * @tparam Func Types of the function objects to invoke. + * @param func Valid function objects. + * @return A reference to the patched element. + */ + template + decltype(auto) patch(Func &&...func) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return owner_or_assert().template patch(entt, std::forward(func)...); + } + + /** + * @brief Replaces the given element for a handle. + * @tparam Type Type of element to replace. + * @tparam Args Types of arguments to use to construct the element. + * @param args Parameters to use to initialize the element. + * @return A reference to the element being replaced. + */ + template + decltype(auto) replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return owner_or_assert().template replace(entt, std::forward(args)...); + } + + /** + * @brief Removes the given elements from a handle. + * @tparam Type Types of elements to remove. + * @return The number of elements actually removed. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + size_type remove() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Type> && ...), "Invalid type"); + return owner_or_assert().template remove(entt); + } + + /** + * @brief Erases the given elements from a handle. + * @tparam Type Types of elements to erase. + */ + template + void erase() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Type> && ...), "Invalid type"); + owner_or_assert().template erase(entt); + } + + /** + * @brief Checks if a handle has all the given elements. + * @tparam Type Elements for which to perform the check. + * @return True if the handle has all the elements, false otherwise. + */ + template + [[nodiscard]] decltype(auto) all_of() const { + return owner_or_assert().template all_of(entt); + } + + /** + * @brief Checks if a handle has at least one of the given elements. + * @tparam Type Elements for which to perform the check. + * @return True if the handle has at least one of the given elements, + * false otherwise. + */ + template + [[nodiscard]] decltype(auto) any_of() const { + return owner_or_assert().template any_of(entt); + } + + /** + * @brief Returns references to the given elements for a handle. + * @tparam Type Types of elements to get. + * @return References to the elements owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Type> && ...), "Invalid type"); + return owner_or_assert().template get(entt); + } + + /** + * @brief Returns a reference to the given element for a handle. + * @tparam Type Type of element to get. + * @tparam Args Types of arguments to use to construct the element. + * @param args Parameters to use to initialize the element. + * @return Reference to the element owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return owner_or_assert().template get_or_emplace(entt, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given elements for a handle. + * @tparam Type Types of elements to get. + * @return Pointers to the elements owned by the handle. + */ + template + [[nodiscard]] auto try_get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Type> && ...), "Invalid type"); + return owner_or_assert().template try_get(entt); + } + + /** + * @brief Checks if a handle has elements assigned. + * @return True if the handle has no elements assigned, false otherwise. + */ + [[nodiscard]] bool orphan() const { + return owner_or_assert().orphan(entt); + } + + /** + * @brief Returns a const handle from a non-const one. + * @tparam Other A valid entity type. + * @tparam Args Scope of the handle to construct. + * @return A const handle referring to the same registry and the same + * entity. + */ + template + operator basic_handle() const noexcept { + static_assert(std::is_same_v || std::is_same_v, Registry>, "Invalid conversion between different handles"); + static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v, Args>))), "Invalid conversion between different handles"); + return owner ? basic_handle{*owner, entt} : basic_handle{}; + } + +private: + registry_type *owner; + entity_type entt; +}; + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same registry and the same + * entity, false otherwise. + */ +template +[[nodiscard]] bool operator==(const basic_handle &lhs, const basic_handle &rhs) noexcept { + return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity(); +} + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same registry and the same + * entity, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const basic_handle &lhs, const basic_handle &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares a handle with the null object. + * @tparam Args Scope of the handle. + * @param lhs A valid handle. + * @param rhs A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const basic_handle &lhs, const null_t rhs) noexcept { + return (lhs.entity() == rhs); +} + +/** + * @brief Compares a handle with the null object. + * @tparam Args Scope of the handle. + * @param lhs A null object yet to be converted. + * @param rhs A valid handle. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const null_t lhs, const basic_handle &rhs) noexcept { + return (rhs == lhs); +} + +/** + * @brief Compares a handle with the null object. + * @tparam Args Scope of the handle. + * @param lhs A valid handle. + * @param rhs A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_handle &lhs, const null_t rhs) noexcept { + return (lhs.entity() != rhs); +} + +/** + * @brief Compares a handle with the null object. + * @tparam Args Scope of the handle. + * @param lhs A null object yet to be converted. + * @param rhs A valid handle. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const null_t lhs, const basic_handle &rhs) noexcept { + return (rhs != lhs); +} + +} // namespace entt + +#endif + +// #include "entity/helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.hpp" + +// #include "component.hpp" +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; + +template<> +struct in_place_delete: std::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; + +template<> +struct page_size: std::integral_constant {}; + +template +struct page_size> + : std::integral_constant {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Element type. + * @tparam Entity A valid entity type. + */ +template +struct component_traits { + static_assert(std::is_same_v, Type>, "Unsupported type"); + + /*! @brief Element type. */ + using element_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ + static constexpr std::size_t page_size = internal::page_size::value; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class extended_group_iterator; + +template +class extended_group_iterator, get_t> { + template + [[nodiscard]] auto index_to_element([[maybe_unused]] Type &cpool) const { + if constexpr(std::is_void_v) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + using iterator_type = It; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_group_iterator() + : it{}, + pools{} {} + + extended_group_iterator(iterator_type from, std::tuple cpools) + : it{from}, + pools{std::move(cpools)} {} + + extended_group_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_group_iterator operator++(int) noexcept { + const extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +struct group_descriptor { + using size_type = std::size_t; + virtual ~group_descriptor() = default; + [[nodiscard]] virtual bool owned(const id_type) const noexcept { + return false; + } +}; + +template +class group_handler final: public group_descriptor { + using entity_type = typename Type::entity_type; + + void swap_elements(const std::size_t pos, const entity_type entt) { + for(size_type next{}; next < Owned; ++next) { + pools[next]->swap_elements((*pools[next])[pos], entt); + } + } + + void push_on_construct(const entity_type entt) { + if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(std::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + swap_elements(len++, entt); + } + } + + void remove_if(const entity_type entt) { + if(pools[0u]->contains(entt) && (pools[0u]->index(entt) < len)) { + swap_elements(--len, entt); + } + } + + void common_setup() { + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto first = pools[0u]->rbegin(), last = first + static_cast(pools[0u]->size()); first != last; ++first) { + push_on_construct(*first); + } + } + +public: + using common_type = Type; + using size_type = typename Type::size_type; + + template + group_handler(std::tuple ogpool, std::tuple epool) + : pools{std::apply([](auto &&...cpool) { return std::array{&cpool...}; }, ogpool)}, + filter{std::apply([](auto &&...cpool) { return std::array{&cpool...}; }, epool)} { + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, ogpool); + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool); + common_setup(); + } + + [[nodiscard]] bool owned(const id_type hash) const noexcept override { + for(size_type pos{}; pos < Owned; ++pos) { + if(pools[pos]->info().hash() == hash) { + return true; + } + } + + return false; + } + + [[nodiscard]] size_type length() const noexcept { + return len; + } + + template + [[nodiscard]] common_type *storage() const noexcept { + if constexpr(Index < (Owned + Get)) { + return pools[Index]; + } else { + return filter[Index - (Owned + Get)]; + } + } + +private: + std::array pools; + std::array filter; + std::size_t len{}; +}; + +template +class group_handler final: public group_descriptor { + using entity_type = typename Type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + elem.push(entt); + } + } + + void remove_if(const entity_type entt) { + elem.remove(entt); + } + + void common_setup() { + for(const auto entity: *pools[0u]) { + push_on_construct(entity); + } + } + +public: + using common_type = Type; + + template + group_handler(const Allocator &allocator, std::tuple gpool, std::tuple epool) + : pools{std::apply([](auto &&...cpool) { return std::array{&cpool...}; }, gpool)}, + filter{std::apply([](auto &&...cpool) { return std::array{&cpool...}; }, epool)}, + elem{allocator} { + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::push_on_construct>(*this), cpool.on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, gpool); + std::apply([this](auto &...cpool) { ((cpool.on_construct().template connect<&group_handler::remove_if>(*this), cpool.on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, epool); + common_setup(); + } + + [[nodiscard]] common_type &handle() noexcept { + return elem; + } + + [[nodiscard]] const common_type &handle() const noexcept { + return elem; + } + + template + [[nodiscard]] common_type *storage() const noexcept { + if constexpr(Index < Get) { + return pools[Index]; + } else { + return filter[Index - Get]; + } + } + +private: + std::array pools; + std::array filter; + common_type elem; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @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 are at + * least in the given storage. Moreover, it's guaranteed that the entity list is + * tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, elements are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + [[nodiscard]] auto pools_for(std::index_sequence) const noexcept { + using return_type = std::tuple; + return descriptor ? return_type{static_cast(descriptor->template storage())...} : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler; + + /** + * @brief Group opaque identifier. + * @return Group opaque identifier. + */ + static id_type group_id() noexcept { + return type_hash, get_t...>, exclude_t...>>>::value(); + } + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return descriptor->handle(); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t>; + return *this ? static_cast(descriptor->template storage()) : nullptr; + } + + /** + * @brief Returns the number of entities that are part of the group. + * @return Number of entities that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? handle().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 noexcept { + return *this ? handle().capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + descriptor->handle().shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || handle().empty(); + } + + /** + * @brief Returns an iterator 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 noexcept { + return *this ? handle().begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? handle().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 noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return *this ? handle().find(entt) : iterator{}; + } + + /** + * @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()[static_cast(pos)]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Type Type of the element to get. + * @tparam Other Other types of elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Index Indexes of the elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools_for(std::index_sequence_for{}); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and elements 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 elements. The + * _constness_ of the elements 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_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty elements. The _constness_ of the + * elements is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools_for(std::index_sequence_for{}); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group 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(std::tuple, std::tuple); + * bool(const Type &..., const Type &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` 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 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 Type Optional type of element to compare. + * @tparam Other Other optional types of elements 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) { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of elements 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...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + descriptor->handle().sort(std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, cpools = pools_for(std::index_sequence_for{})](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + descriptor->handle().sort(std::move(comp), std::move(algo), std::forward(args)...); + } + } + } + + /** + * @brief Sort entities according to their order in a range. + * + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. + * + * @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 sort_as(It first, It last) const { + if(*this) { + descriptor->handle().sort_as(first, last); + } + } + +private: + handler *descriptor; +}; + +/** + * @brief Owning group. + * + * Owning groups returns all entities and only the entities that are at + * least in the given storage. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that all elements in the owned storage are tightly packed + * in memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned storage and all instances have the + * same order in memory. + * + * The more types of storage are owned, the faster it is to iterate a group. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, elements are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + static_assert(((Owned::storage_policy != deletion_policy::in_place) && ...), "Groups do not support in-place delete"); + + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + [[nodiscard]] auto pools_for(std::index_sequence, std::index_sequence) const noexcept { + using return_type = std::tuple; + return descriptor ? return_type{static_cast(descriptor->template storage())..., static_cast(descriptor->template storage())...} : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler; + + /** + * @brief Group opaque identifier. + * @return Group opaque identifier. + */ + static id_type group_id() noexcept { + return type_hash...>, get_t...>, exclude_t...>>>::value(); + } + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return *storage<0>(); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t>; + return *this ? static_cast(descriptor->template storage()) : nullptr; + } + + /** + * @brief Returns the number of entities that that are part of the group. + * @return Number of entities that that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? descriptor->length() : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || !descriptor->length(); + } + + /** + * @brief Returns an iterator 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 noexcept { + return *this ? (handle().end() - static_cast(descriptor->length())) : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? (handle().rbegin() + static_cast(descriptor->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 noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + const auto it = *this ? handle().find(entt) : iterator{}; + return it >= begin() ? it : iterator{}; + } + + /** + * @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()[static_cast(pos)]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Type Type of the element to get. + * @tparam Other Other types of elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Index Indexes of the elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + const auto cpools = pools_for(std::index_sequence_for{}, std::index_sequence_for{}); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and elements 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 elements. The + * _constness_ of the elements 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_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty elements. The _constness_ of the + * elements is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools_for(std::index_sequence_for{}, std::index_sequence_for{}); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group 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(std::tuple, std::tuple); + * bool(const Type &, const Type &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` 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 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 Type Optional type of element to compare. + * @tparam Other Other optional types of elements 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 { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes of elements 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 { + const auto cpools = pools_for(std::index_sequence_for{}, std::index_sequence_for{}); + + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward(args)...); + } + + auto cb = [this](auto *head, auto *...other) { + for(auto next = descriptor->length(); next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }; + + std::apply(cb, cpools); + } + +private: + handler *descriptor; +}; + +} // namespace entt + +#endif + +// #include "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/bit.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.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 "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#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 +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr const char *pretty_function() noexcept { +#if defined ENTT_PRETTY_FUNCTION + return static_cast(ENTT_PRETTY_FUNCTION); +#else + return ""; +#endif +} + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + const std::string_view full_name{pretty_function()}; + auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static const type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "utility.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_request : std::uint8_t { + info, + transfer, + assign, + compare, + copy, + move +}; + +template +struct basic_any_storage { + static constexpr bool has_buffer = true; + union { + const void *instance{}; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + alignas(Align) std::byte buffer[Len]; + }; +}; + +template +struct basic_any_storage<0u, Align> { + static constexpr bool has_buffer = false; + const void *instance{}; +}; + +template +// NOLINTNEXTLINE(bugprone-sizeof-expression) +struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v> {}; + +template +struct in_situ: std::false_type {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any: private internal::basic_any_storage { + using request = internal::any_request; + using base_type = internal::basic_any_storage; + using vtable_type = const void *(const request, const basic_any &, const void *); + using deleter_type = void(const basic_any &); + + template + static constexpr bool in_situ_v = internal::in_situ::value; + + template + static const void *basic_vtable(const request req, const basic_any &value, const void *other) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + + switch(const auto *elem = static_cast(value.data()); req) { + case request::info: + return &type_id(); + case request::transfer: + if constexpr(std::is_move_assignable_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + *const_cast(elem) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case request::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(elem) = *static_cast(other); + return other; + } + break; + case request::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return (*elem == *static_cast(other)) ? other : nullptr; + } else { + return (elem == other) ? other : nullptr; + } + case request::copy: + if constexpr(std::is_copy_constructible_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + static_cast(const_cast(other))->initialize(*elem); + } + break; + case request::move: + ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy"); + if constexpr(in_situ_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion) + return ::new(&static_cast(const_cast(other))->buffer) Type{std::move(*const_cast(elem))}; + } + } + + return nullptr; + } + + template + static void basic_deleter(const basic_any &value) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !std::is_trivially_destructible_v), "Unexpected policy"); + + const auto *elem = static_cast(value.data()); + + if constexpr(in_situ_v) { + (value.mode == any_policy::embedded) ? elem->~Type() : (delete elem); + } else if constexpr(std::is_array_v) { + delete[] elem; + } else { + delete elem; + } + } + + template + void initialize([[maybe_unused]] Args &&...args) { + using plain_type = std::remove_const_t>; + + vtable = basic_vtable; + underlying_type = type_hash::value(); + + if constexpr(std::is_void_v) { + deleter = nullptr; + mode = any_policy::empty; + this->instance = nullptr; + } else if constexpr(std::is_lvalue_reference_v) { + deleter = nullptr; + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) + this->instance = (std::addressof(args), ...); + } else if constexpr(in_situ_v) { + if constexpr(std::is_trivially_destructible_v) { + deleter = nullptr; + } else { + deleter = &basic_deleter; + } + + mode = any_policy::embedded; + + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + ::new(&this->buffer) plain_type{std::forward(args)...}; + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + ::new(&this->buffer) plain_type(std::forward(args)...); + } + } else { + deleter = &basic_deleter; + mode = any_policy::dynamic; + + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + this->instance = new plain_type{std::forward(args)...}; + } else if constexpr(std::is_array_v) { + static_assert(sizeof...(Args) == 0u, "Invalid arguments"); + this->instance = new plain_type[std::extent_v](); + } else { + this->instance = new plain_type(std::forward(args)...); + } + } + } + + void invoke_deleter_if_exists() { + if(deleter != nullptr) { + deleter(*this); + } + } + +public: + /*! @brief Size of the internal buffer. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : base_type{} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value A pointer to an object to take ownership of. + */ + template + explicit basic_any(std::in_place_t, Type *value) + : base_type{} { + static_assert(!std::is_const_v && !std::is_void_v, "Non-const non-void pointer required"); + + if(value == nullptr) { + initialize(); + } else { + initialize(*value); + deleter = &basic_deleter; + mode = any_policy::dynamic; + } + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, 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{} { + other.vtable(request::copy, other, this); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : base_type{}, + vtable{other.vtable}, + deleter{other.deleter}, + underlying_type{other.underlying_type}, + mode{other.mode} { + if(other.mode == any_policy::embedded) { + other.vtable(request::move, other, this); + } else if(other.mode != any_policy::empty) { + this->instance = std::exchange(other.instance, nullptr); + } + } + + /*! @brief Frees the internal buffer, whatever it means. */ + ~basic_any() { + invoke_deleter_if_exists(); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + if(this != &other) { + invoke_deleter_if_exists(); + + if(other) { + other.vtable(request::copy, other, this); + } else { + initialize(); + } + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + if(this != &other) { + invoke_deleter_if_exists(); + + if(other.mode == any_policy::embedded) { + other.vtable(request::move, other, this); + } else if(other.mode != any_policy::empty) { + this->instance = std::exchange(other.instance, nullptr); + } + + vtable = other.vtable; + deleter = other.deleter; + underlying_type = other.underlying_type; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template, basic_any>>> + basic_any &operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] bool has_value() const noexcept { + return (mode != any_policy::empty); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @param req Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + [[nodiscard]] bool has_value(const type_info &req) const noexcept { + return (underlying_type == req.hash()); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @tparam Type Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + template + [[nodiscard]] bool has_value() const noexcept { + static_assert(std::is_same_v, Type>, "Invalid type"); + return (underlying_type == type_hash::value()); + } + + /** + * @brief Returns the object type info if any, `type_id()` otherwise. + * @return The object type info if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *static_cast(vtable(request::info, *this, nullptr)); + } + + /*! @copydoc info */ + [[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept { + return info(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + if constexpr(base_type::has_buffer) { + return (mode == any_policy::embedded) ? &this->buffer : this->instance; + } else { + return this->instance; + } + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return has_value(req) ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @tparam Type Expected type. + * @return An opaque pointer the contained instance, if any. + */ + template + [[nodiscard]] const Type *data() const noexcept { + return has_value>() ? static_cast(data()) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @tparam Type Expected type. + * @return An opaque pointer the contained instance, if any. + */ + template + [[nodiscard]] Type *data() noexcept { + if constexpr(std::is_const_v) { + return std::as_const(*this).template data>(); + } else { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).template data>()); + } + } + + /** + * @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) { + invoke_deleter_if_exists(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) { + return (vtable(request::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + bool assign(basic_any &&other) { + if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) { + return (other.mode == any_policy::cref) ? (vtable(request::assign, *this, std::as_const(other).data()) != nullptr) : (vtable(request::transfer, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @brief Destroys contained object */ + void reset() { + invoke_deleter_if_exists(); + initialize(); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return has_value(); + } + + /** + * @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 basic_any &other) const noexcept { + if(other && (underlying_type == other.underlying_type)) { + return (vtable(request::compare, *this, other.data()) != nullptr); + } + + return (!*this && !other); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + basic_any other = std::as_const(*this).as_ref(); + other.mode = (mode == any_policy::cref ? any_policy::cref : any_policy::ref); + return other; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + basic_any other{}; + other.instance = data(); + other.vtable = vtable; + other.underlying_type = underlying_type; + other.mode = any_policy::cref; + return other; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::dynamic || mode == any_policy::embedded); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + vtable_type *vtable{}; + deleter_type *deleter{}; + id_type underlying_type{}; + any_policy mode{}; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] std::remove_const_t any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] std::remove_const_t any_cast(basic_any &data) 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, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) +[[nodiscard]] std::remove_const_t any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } + + return any_cast(data); + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + return data->template data>(); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + return data->template data(); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/bit.hpp" + +// #include "../core/type_info.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct sparse_set_iterator final { + using value_type = typename Container::value_type; + using pointer = typename Container::const_pointer; + using reference = typename Container::const_reference; + using difference_type = typename Container::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr sparse_set_iterator() noexcept + : packed{}, + offset{} {} + + constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept + : packed{&ref}, + offset{idx} {} + + constexpr sparse_set_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr sparse_set_iterator operator++(int) noexcept { + const sparse_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr sparse_set_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr sparse_set_iterator operator--(int) noexcept { + const sparse_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { + sparse_set_iterator copy = *this; + return (copy += value); + } + + constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return (*packed)[static_cast(index() - value)]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr pointer data() const noexcept { + return packed ? packed->data() : nullptr; + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + const Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief 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 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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector; + using traits_type = entt_traits; + + static constexpr auto max_size = static_cast(traits_type::to_entity(null)); + + // it could be auto but gcc complains and emits a warning due to a false positive + [[nodiscard]] std::size_t policy_to_head() const noexcept { + return static_cast(max_size * static_cast>(mode != deletion_policy::swap_only)); + } + + [[nodiscard]] auto entity_to_pos(const Entity entt) const noexcept { + return static_cast(traits_type::to_entity(entt)); + } + + [[nodiscard]] auto pos_to_page(const std::size_t pos) const noexcept { + return static_cast(pos / traits_type::page_size); + } + + [[nodiscard]] auto sparse_ptr(const Entity entt) const { + const auto pos = entity_to_pos(entt); + const auto page = pos_to_page(pos); + return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; + } + + [[nodiscard]] auto &sparse_ref(const Entity entt) const { + ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); + const auto pos = entity_to_pos(entt); + return sparse[pos_to_page(pos)][fast_mod(pos, traits_type::page_size)]; + } + + [[nodiscard]] auto to_iterator(const Entity entt) const { + return --(end() - static_cast(index(entt))); + } + + [[nodiscard]] auto &assure_at_least(const Entity entt) { + const auto pos = entity_to_pos(entt); + const auto page = pos_to_page(pos); + + if(!(page < sparse.size())) { + sparse.resize(page + 1u, nullptr); + } + + if(!sparse[page]) { + constexpr entity_type init = null; + auto page_allocator{packed.get_allocator()}; + sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); + std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init); + } + + return sparse[page][fast_mod(pos, traits_type::page_size)]; + } + + void release_sparse_pages() { + auto page_allocator{packed.get_allocator()}; + + for(auto &&page: sparse) { + if(page != nullptr) { + std::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); + page = nullptr; + } + } + } + + void swap_at(const std::size_t lhs, const std::size_t rhs) { + auto &from = packed[lhs]; + auto &to = packed[rhs]; + + sparse_ref(from) = traits_type::combine(static_cast(rhs), traits_type::to_integral(from)); + sparse_ref(to) = traits_type::combine(static_cast(lhs), traits_type::to_integral(to)); + + std::swap(from, to); + } + +private: + [[nodiscard]] virtual const void *get_at(const std::size_t) const { + return nullptr; + } + + virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) { + ENTT_ASSERT((mode != deletion_policy::swap_only) || ((lhs < head) == (rhs < head)), "Cross swapping is not supported"); + } + +protected: + /*! @brief Random access iterator type. */ + using basic_iterator = internal::sparse_set_iterator; + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_only(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch"); + const auto pos = index(*it); + bump(traits_type::next(*it)); + swap_at(pos, head -= (pos < head)); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_and_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch"); + auto &self = sparse_ref(*it); + const auto entt = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); + packed[static_cast(entt)] = packed.back(); + // unnecessary but it helps to detect nasty bugs + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT((packed.back() = null, true), ""); + // lazy self-assignment guard + self = null; + packed.pop_back(); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void in_place_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch"); + const auto pos = entity_to_pos(std::exchange(sparse_ref(*it), null)); + packed[pos] = traits_type::combine(static_cast(std::exchange(head, pos)), tombstone); + } + + /** + * @brief Erases entities from a sparse set. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + virtual void pop(basic_iterator first, basic_iterator last) { + switch(mode) { + case deletion_policy::swap_and_pop: + for(; first != last; ++first) { + swap_and_pop(first); + } + break; + case deletion_policy::in_place: + for(; first != last; ++first) { + in_place_pop(first); + } + break; + case deletion_policy::swap_only: + for(; first != last; ++first) { + swap_only(first); + } + break; + } + } + + /*! @brief Erases all entities of a sparse set. */ + virtual void pop_all() { + switch(mode) { + case deletion_policy::in_place: + if(head != max_size) { + for(auto &&elem: packed) { + if(elem != tombstone) { + sparse_ref(elem) = null; + } + } + break; + } + [[fallthrough]]; + case deletion_policy::swap_only: + case deletion_policy::swap_and_pop: + for(auto &&elem: packed) { + sparse_ref(elem) = null; + } + break; + } + + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Assigns an entity to a sparse set. + * @param entt A valid identifier. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) { + ENTT_ASSERT(entt != null && entt != tombstone, "Invalid element"); + auto &elem = assure_at_least(entt); + auto pos = size(); + + switch(mode) { + case deletion_policy::in_place: + if(head != max_size && !force_back) { + pos = head; + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast(head), traits_type::to_integral(entt)); + head = entity_to_pos(std::exchange(packed[pos], entt)); + break; + } + [[fallthrough]]; + case deletion_policy::swap_and_pop: + packed.push_back(entt); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + break; + case deletion_policy::swap_only: + if(elem == null) { + packed.push_back(entt); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + } else { + ENTT_ASSERT(!(entity_to_pos(elem) < head), "Slot not available"); + bump(entt); + } + + pos = head++; + swap_at(entity_to_pos(elem), pos); + break; + } + + return iterator{packed, static_cast(++pos)}; + } + + /*! @brief Forwards variables to derived classes, if any. */ + // NOLINTNEXTLINE(performance-unnecessary-value-param) + virtual void bind_any(any) noexcept {} + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Pointer type to contained entities. */ + using pointer = typename packed_container_type::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = basic_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + + /*! @brief Default constructor. */ + basic_sparse_set() + : basic_sparse_set{type_id()} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sparse_set(const allocator_type &allocator) + : basic_sparse_set{deletion_policy::swap_and_pop, allocator} {} + + /** + * @brief Constructs an empty container with the given policy and allocator. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {}) + : basic_sparse_set{type_id(), pol, allocator} {} + + /** + * @brief Constructs an empty container with the given value type, policy + * and allocator. + * @param elem Returned value type, if any. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) + : sparse{allocator}, + packed{allocator}, + descriptor{&elem}, + mode{pol}, + head{policy_to_head()} { + ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions"); + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_sparse_set(const basic_sparse_set &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sparse_set(basic_sparse_set &&other) noexcept + : sparse{std::move(other.sparse)}, + packed{std::move(other.packed)}, + descriptor{other.descriptor}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) + : sparse{std::move(other.sparse), allocator}, + packed{std::move(other.packed), allocator}, + descriptor{other.descriptor}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed"); + } + + /*! @brief Default destructor. */ + virtual ~basic_sparse_set() { + release_sparse_pages(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This sparse set. + */ + basic_sparse_set &operator=(const basic_sparse_set &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This sparse set. + */ + basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed"); + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given sparse set. + * @param other Sparse set to exchange the content with. + */ + void swap(basic_sparse_set &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(descriptor, other.descriptor); + swap(mode, other.mode); + swap(head, other.head); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return packed.get_allocator(); + } + + /** + * @brief Returns the deletion policy of a sparse set. + * @return The deletion policy of the sparse set. + */ + [[nodiscard]] deletion_policy policy() const noexcept { + return mode; + } + + /** + * @brief Returns data on the free list whose meaning depends on the mode. + * @return Free list information that is mode dependent. + */ + [[nodiscard]] size_type free_list() const noexcept { + return head; + } + + /** + * @brief Sets data on the free list whose meaning depends on the mode. + * @param value Free list information that is mode dependent. + */ + void free_list(const size_type value) noexcept { + ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value"); + head = value; + } + + /** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + virtual void reserve(const size_type cap) { + packed.reserve(cap); + } + + /** + * @brief Returns the number of elements that a sparse set has currently + * allocated space for. + * @return Capacity of the sparse set. + */ + [[nodiscard]] virtual size_type capacity() const noexcept { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + virtual void shrink_to_fit() { + sparse_container_type other{sparse.get_allocator()}; + const auto len = sparse.size(); + size_type cnt{}; + + other.reserve(len); + + for(auto &&elem: std::as_const(packed)) { + if(elem != tombstone) { + if(const auto page = pos_to_page(entity_to_pos(elem)); sparse[page] != nullptr) { + if(const auto sz = page + 1u; sz > other.size()) { + other.resize(sz, nullptr); + } + + other[page] = std::exchange(sparse[page], nullptr); + + if(++cnt == len) { + // early exit due to lack of pages + break; + } + } + } + } + + release_sparse_pages(); + sparse.swap(other); + + 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 all pages have been allocated, nor that the + * internal packed array is be the same size. + * + * @return Extent of the sparse set. + */ + [[nodiscard]] size_type extent() const noexcept { + return sparse.size() * traits_type::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 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 noexcept { + return packed.empty(); + } + + /** + * @brief Checks whether a sparse set is fully packed. + * @return True if the sparse set is fully packed, false otherwise. + */ + [[nodiscard]] bool contiguous() const noexcept { + return (mode != deletion_policy::in_place) || (head == max_size); + } + + /** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ + [[nodiscard]] pointer data() const noexcept { + return packed.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @return An iterator to the first entity of the sparse set. + */ + [[nodiscard]] iterator begin() const noexcept { + const auto pos = static_cast(packed.size()); + return iterator{packed, pos}; + } + + /*! @copydoc begin */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last entity of a sparse + * set. + */ + [[nodiscard]] iterator end() const noexcept { + return iterator{packed, {}}; + } + + /*! @copydoc end */ + [[nodiscard]] const_iterator cend() const noexcept { + return end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * 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 noexcept { + return std::make_reverse_iterator(end()); + } + + /*! @copydoc rbegin */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last entity of the + * reversed sparse set. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return std::make_reverse_iterator(begin()); + } + + /*! @copydoc rend */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return rend(); + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] const_iterator find(const entity_type entt) const noexcept { + return contains(entt) ? to_iterator(entt) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + const auto *elem = sparse_ptr(entt); + constexpr auto cap = traits_type::entity_mask; + constexpr auto mask = traits_type::to_integral(null) & ~cap; + // testing versions permits to avoid accessing the packed array + return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); + } + + /** + * @brief Returns the contained version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if present, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const noexcept { + const auto *elem = sparse_ptr(entt); + constexpr auto fallback = traits_type::to_version(tombstone); + return elem ? traits_type::to_version(*elem) : fallback; + } + + /** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const noexcept { + ENTT_ASSERT(contains(entt), "Set does not contain entity"); + return entity_to_pos(sparse_ref(entt)); + } + + /** + * @brief Returns the entity at specified location. + * @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 noexcept { + ENTT_ASSERT(pos < packed.size(), "Index out of bounds"); + return packed[pos]; + } + + /** + * @brief Returns the element assigned to an entity, if any. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior. + * + * @param entt A valid identifier. + * @return An opaque pointer to the element assigned to the entity, if any. + */ + [[nodiscard]] const void *value(const entity_type entt) const noexcept { + return get_at(index(entt)); + } + + /*! @copydoc value */ + [[nodiscard]] void *value(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).value(entt)); + } + + /** + * @brief Assigns an entity to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + * @param elem Optional opaque element to forward to mixins, if any. + * @return Iterator pointing to the emplaced element in case of success, the + * `end()` iterator otherwise. + */ + iterator push(const entity_type entt, const void *elem = nullptr) { + return try_emplace(entt, false, elem); + } + + /** + * @brief Assigns one or more entities to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return Iterator pointing to the first element inserted in case of + * success, the `end()` iterator otherwise. + */ + template + iterator push(It first, It last) { + auto curr = end(); + + for(; first != last; ++first) { + curr = try_emplace(*first, true); + } + + return curr; + } + + /** + * @brief Bump the version number of an entity. + * + * @warning + * Attempting to bump the version of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the given identifier. + */ + version_type bump(const entity_type entt) { + auto &elem = sparse_ref(entt); + ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version"); + elem = traits_type::combine(traits_type::to_integral(elem), traits_type::to_integral(entt)); + packed[entity_to_pos(elem)] = entt; + return traits_type::to_version(entt); + } + + /** + * @brief Erases an entity from a sparse set. + * + * @warning + * Attempting to erase an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + */ + void erase(const entity_type entt) { + const auto it = to_iterator(entt); + pop(it, it + 1u); + } + + /** + * @brief Erases entities from a set. + * + * @sa erase + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void erase(It first, It last) { + if constexpr(std::is_same_v) { + pop(first, last); + } else { + for(; first != last; ++first) { + erase(*first); + } + } + } + + /** + * @brief Removes an entity from a sparse set if it exists. + * @param entt A valid identifier. + * @return True if the entity is actually removed, false otherwise. + */ + bool remove(const entity_type entt) { + return contains(entt) && (erase(entt), true); + } + + /** + * @brief Removes entities from a sparse set if they exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of entities actually removed. + */ + template + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v) { + while(first != last) { + while(first != last && !contains(*first)) { + ++first; + } + + const auto it = first; + + while(first != last && contains(*first)) { + ++first; + } + + count += static_cast(std::distance(it, first)); + erase(it, first); + } + } else { + for(; first != last; ++first) { + count += remove(*first); + } + } + + return count; + } + + /*! @brief Removes all tombstones from a sparse set. */ + void compact() { + if(mode == deletion_policy::in_place) { + size_type from = packed.size(); + size_type pos = std::exchange(head, max_size); + + for(; from && packed[from - 1u] == tombstone; --from) {} + + while(pos != max_size) { + if(const auto to = std::exchange(pos, entity_to_pos(packed[pos])); to < from) { + --from; + swap_or_move(from, to); + + packed[to] = packed[from]; + const auto elem = static_cast(to); + sparse_ref(packed[to]) = traits_type::combine(elem, traits_type::to_integral(packed[to])); + + for(; from && packed[from - 1u] == tombstone; --from) {} + } + } + + packed.erase(packed.begin() + static_cast(from), packed.end()); + } + } + + /** + * @brief Swaps two entities in a sparse set. + * + * For what it's worth, this function affects both the internal sparse array + * and the internal packed array. Users should not care of that anyway. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior. + * + * @param lhs A valid identifier. + * @param rhs A valid identifier. + */ + void swap_elements(const entity_type lhs, const entity_type rhs) { + const auto from = index(lhs); + const auto to = index(rhs); + + // basic no-leak guarantee if swapping throws + swap_or_move(from, 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 length Number of elements to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed"); + ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); + + algo(packed.rend() - static_cast(length), packed.rend(), std::move(compare), std::forward(args)...); + + for(size_type pos{}; pos < length; ++pos) { + auto curr = pos; + auto next = index(packed[curr]); + + while(curr != next) { + const auto idx = index(packed[next]); + const auto entt = packed[curr]; + + swap_or_move(next, idx); + const auto elem = static_cast(curr); + sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr])); + curr = std::exchange(next, idx); + } + } + } + + /** + * @brief Sort all elements according to the given comparison function. + * + * @sa sort_n + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size(); + sort_n(len, std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort entities according to their order in a range. + * + * Entities that are part of both the sparse set and the range are ordered + * internally according to the order they have in the range.
+ * All other entities goes to the end of the sparse set and there are no + * guarantees on their order. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return An iterator past the last of the elements actually shared. + */ + template + iterator sort_as(It first, It last) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed"); + const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size(); + auto it = end() - static_cast(len); + + for(const auto other = end(); (it != other) && (first != last); ++first) { + if(const auto curr = *first; contains(curr)) { + if(const auto entt = *it; entt != curr) { + // basic no-leak guarantee (with invalid state) if swapping throws + swap_elements(entt, curr); + } + + ++it; + } + } + + return it; + } + + /*! @brief Clears a sparse set. */ + void clear() { + pop_all(); + // sanity check to avoid subtle issues due to storage classes + ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Returns a type info object for the value type, if any. + * @return A type info object for the value type, if any. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *descriptor; + } + + /*! @copydoc info */ + [[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept { + return info(); + } + + /** + * @brief Forwards variables to derived classes, if any. + * @tparam Type Type of the element to forward. + * @param value The element to forward. + */ + template + void bind(Type &&value) noexcept { + bind_any(forward_as_any(std::forward(value))); + } + +private: + sparse_container_type sparse; + packed_container_type packed; + const type_info *descriptor; + deletion_policy mode; + size_type head; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class storage_iterator final { + friend storage_iterator; + + using container_type = std::remove_const_t; + using alloc_traits = std::allocator_traits; + + using iterator_traits = std::iterator_traits, + typename alloc_traits::template rebind_traits::element_type>::const_pointer, + typename alloc_traits::template rebind_traits::element_type>::pointer>>; + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr storage_iterator() noexcept = default; + + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept + : payload{ref}, + offset{idx} {} + + template, typename = std::enable_if_t> + constexpr storage_iterator(const storage_iterator, Page> &other) noexcept + : storage_iterator{other.payload, other.offset} {} + + constexpr storage_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr storage_iterator operator++(int) noexcept { + const storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr storage_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr storage_iterator operator--(int) noexcept { + const storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr storage_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr storage_iterator operator+(const difference_type value) const noexcept { + storage_iterator copy = *this; + return (copy += value); + } + + constexpr storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + const auto pos = static_cast(index() - value); + return (*payload)[pos / Page][fast_mod(static_cast(pos), Page)]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + Container *payload; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class extended_storage_iterator final { + template + friend class extended_storage_iterator; + +public: + using iterator_type = It; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::forward_as_tuple(*std::declval()...))); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_storage_iterator() + : it{} {} + + constexpr extended_storage_iterator(iterator_type base, Other... other) + : it{base, other...} {} + + template && ...) && (std::is_constructible_v && ...)>> + constexpr extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} + + constexpr extended_storage_iterator &operator++() noexcept { + return ++std::get(it), (++std::get(it), ...), *this; + } + + constexpr extended_storage_iterator operator++(int) noexcept { + const extended_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {*std::get(it), *std::get(it)...}; + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return std::get(it); + } + + template + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; + +private: + std::tuple it; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @tparam Type Element type. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage: public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + using traits_type = component_traits; + + [[nodiscard]] auto &element_at(const std::size_t pos) const { + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + auto assure_at_least(const std::size_t pos) { + const auto idx = pos / traits_type::page_size; + + if(!(idx < payload.size())) { + auto curr = payload.size(); + allocator_type allocator{get_allocator()}; + payload.resize(idx + 1u, nullptr); + + ENTT_TRY { + for(const auto last = payload.size(); curr < last; ++curr) { + payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); + } + } + ENTT_CATCH { + payload.resize(curr); + ENTT_THROW; + } + } + + return payload[idx] + fast_mod(pos, traits_type::page_size); + } + + template + auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { + const auto it = base_type::try_emplace(entt, force_back); + + ENTT_TRY { + auto *elem = to_address(assure_at_least(static_cast(it.index()))); + entt::uninitialized_construct_using_allocator(elem, get_allocator(), std::forward(args)...); + } + ENTT_CATCH { + base_type::pop(it, it + 1u); + ENTT_THROW; + } + + return it; + } + + void shrink_to_size(const std::size_t sz) { + const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; + allocator_type allocator{get_allocator()}; + + for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { + if constexpr(traits_type::in_place_delete) { + if(base_type::data()[pos] != tombstone) { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } else { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } + + for(auto pos = from, last = payload.size(); pos < last; ++pos) { + alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size); + } + + payload.resize(from); + payload.shrink_to_fit(); + } + + void swap_at(const std::size_t lhs, const std::size_t rhs) { + using std::swap; + swap(element_at(lhs), element_at(rhs)); + } + + void move_to(const std::size_t lhs, const std::size_t rhs) { + auto &elem = element_at(lhs); + allocator_type allocator{get_allocator()}; + entt::uninitialized_construct_using_allocator(to_address(assure_at_least(rhs)), allocator, std::move(elem)); + alloc_traits::destroy(allocator, std::addressof(elem)); + } + +private: + [[nodiscard]] const void *get_at(const std::size_t pos) const final { + return std::addressof(element_at(pos)); + } + + void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { + static constexpr bool is_pinned_type = !(std::is_move_constructible_v && std::is_move_assignable_v); + // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy + ENTT_ASSERT((from + 1u) && !is_pinned_type, "Pinned type"); + + if constexpr(!is_pinned_type) { + if constexpr(traits_type::in_place_delete) { + (base_type::operator[](to) == tombstone) ? move_to(from, to) : swap_at(from, to); + } else { + swap_at(from, to); + } + } + } + +protected: + /** + * @brief Erases entities from a storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + void pop(underlying_iterator first, underlying_iterator last) override { + for(allocator_type allocator{get_allocator()}; first != last; ++first) { + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); + + if constexpr(traits_type::in_place_delete) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(elem)); + } else { + auto &other = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); + alloc_traits::destroy(allocator, std::addressof(other)); + base_type::swap_and_pop(first); + } + } + } + + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + allocator_type allocator{get_allocator()}; + + for(auto first = base_type::begin(); !(first.index() < 0); ++first) { + if constexpr(traits_type::in_place_delete) { + if(*first != tombstone) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } else { + base_type::swap_and_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } + } + + /** + * @brief Assigns an entity to a storage. + * @param entt A valid identifier. + * @param value Optional opaque value. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { + if(value != nullptr) { + if constexpr(std::is_copy_constructible_v) { + return emplace_element(entt, force_back, *static_cast(value)); + } else { + return base_type::end(); + } + } else { + if constexpr(std::is_default_constructible_v) { + return emplace_element(entt, force_back); + } else { + return base_type::end(); + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Base type. */ + using base_type = underlying_type; + /*! @brief Element type. */ + using element_type = Type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = element_type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Pointer type to contained elements. */ + using pointer = typename container_type::pointer; + /*! @brief Constant pointer type to contained elements. */ + using const_pointer = typename alloc_traits::template rebind_traits::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = internal::storage_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::storage_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + /*! @brief Storage deletion policy. */ + static constexpr deletion_policy storage_policy{traits_type::in_place_delete}; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), storage_policy, allocator}, + payload{allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_storage(const basic_storage &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{static_cast(other)}, + payload{std::move(other.payload)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) + : base_type{static_cast(other), allocator}, + payload{std::move(other.payload), allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed"); + } + + /*! @brief Default destructor. */ + // NOLINTNEXTLINE(bugprone-exception-escape) + ~basic_storage() override { + shrink_to_size(0u); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This storage. + */ + basic_storage &operator=(const basic_storage &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed"); + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_storage &other) noexcept { + using std::swap; + swap(payload, other.payload); + base_type::swap(other); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return payload.get_allocator(); + } + + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) override { + if(cap != 0u) { + base_type::reserve(cap); + assure_at_least(cap - 1u); + } + } + + /** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ + [[nodiscard]] size_type capacity() const noexcept override { + return payload.size() * traits_type::page_size; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() override { + base_type::shrink_to_fit(); + shrink_to_size(base_type::size()); + } + + /** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ + [[nodiscard]] const_pointer raw() const noexcept { + return payload.data(); + } + + /*! @copydoc raw */ + [[nodiscard]] pointer raw() noexcept { + return payload.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + const auto pos = static_cast(base_type::size()); + return const_iterator{&payload, pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + const auto pos = static_cast(base_type::size()); + return iterator{&payload, pos}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return const_iterator{&payload, {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return iterator{&payload, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * 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 noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { + return element_at(base_type::index(entt)); + } + + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { + return std::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { + return std::forward_as_tuple(get(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ + template + value_type &emplace(const entity_type entt, Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + const auto it = emplace_element(entt, false, Type{std::forward(args)...}); + return element_at(static_cast(it.index())); + } else { + const auto it = emplace_element(entt, false, std::forward(args)...); + return element_at(static_cast(it.index())); + } + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ + template + value_type &patch(const entity_type entt, Func &&...func) { + const auto idx = base_type::index(entt); + auto &elem = element_at(idx); + (std::forward(func)(elem), ...); + return elem; + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the object to construct. + * @return Iterator pointing to the first element inserted, if any. + */ + template + iterator insert(It first, It last, const value_type &value = {}) { + for(; first != last; ++first) { + emplace_element(*first, true, value); + } + + return begin(); + } + + /** + * @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. + * @return Iterator pointing to the first element inserted, if any. + */ + template::value_type, value_type>>> + iterator insert(EIt first, EIt last, CIt from) { + for(; first != last; ++first, ++from) { + emplace_element(*first, true, *from); + } + + return begin(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its element. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return iterable{{base_type::begin(), begin()}, {base_type::end(), end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return const_iterable{{base_type::cbegin(), cbegin()}, {base_type::cend(), cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return reverse_iterable{{base_type::rbegin(), rbegin()}, {base_type::rend(), rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return const_reverse_iterable{{base_type::crbegin(), crbegin()}, {base_type::crend(), crend()}}; + } + +private: + container_type payload; +}; + +/*! @copydoc basic_storage */ +template +class basic_storage::page_size == 0u>> + : public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using traits_type = component_traits; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Base type. */ + using base_type = basic_sparse_set>; + /*! @brief Element type. */ + using element_type = Type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = void; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + /*! @brief Storage deletion policy. */ + static constexpr deletion_policy storage_policy{traits_type::in_place_delete}; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), storage_policy, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_storage(const basic_storage &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) + : base_type{std::move(other), allocator} {} + + /*! @brief Default destructor. */ + ~basic_storage() override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This storage. + */ + basic_storage &operator=(const basic_storage &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + // std::allocator has no cross constructors (waiting for C++20) + if constexpr(std::is_void_v && !std::is_constructible_v) { + return allocator_type{}; + } else { + return allocator_type{base_type::get_allocator()}; + } + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + } + + /** + * @brief Returns an empty tuple. + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + return std::tuple{}; + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @param entt A valid identifier. + */ + void emplace(const entity_type entt) { + base_type::try_emplace(entt, false); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns entities to a storage. + * @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) { + for(; first != last; ++first) { + base_type::try_emplace(*first, true); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return iterable{base_type::begin(), base_type::end()}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return const_iterable{base_type::cbegin(), base_type::cend()}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return reverse_iterable{{base_type::rbegin()}, {base_type::rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return const_reverse_iterable{{base_type::crbegin()}, {base_type::crend()}}; + } +}; + +/** + * @brief Swap-only entity storage specialization. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage + : public basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using underlying_iterator = typename basic_sparse_set::basic_iterator; + using traits_type = entt_traits; + + auto from_placeholder() noexcept { + const auto entt = traits_type::combine(static_cast(placeholder), {}); + ENTT_ASSERT(entt != null, "No more entities available"); + placeholder += static_cast(entt != null); + return entt; + } + + auto next() noexcept { + entity_type entt = from_placeholder(); + + while(base_type::current(entt) != traits_type::to_version(tombstone) && entt != null) { + entt = from_placeholder(); + } + + return entt; + } + +protected: + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + base_type::pop_all(); + placeholder = {}; + } + + /** + * @brief Assigns an entity to a storage. + * @param hint A valid identifier. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { + return base_type::find(generate(hint)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Base type. */ + using base_type = basic_sparse_set; + /*! @brief Element type. */ + using element_type = Entity; + /*! @brief Type of the objects assigned to entities. */ + using value_type = void; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + /*! @brief Storage deletion policy. */ + static constexpr deletion_policy storage_policy = deletion_policy::swap_only; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), storage_policy, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_storage(const basic_storage &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + basic_storage(basic_storage &&other) noexcept + : base_type{static_cast(other)}, + placeholder{other.placeholder} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + basic_storage(basic_storage &&other, const allocator_type &allocator) + : base_type{static_cast(other), allocator}, + placeholder{other.placeholder} {} + + /*! @brief Default destructor. */ + ~basic_storage() override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This storage. + */ + basic_storage &operator=(const basic_storage &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + placeholder = other.placeholder; + base_type::operator=(std::move(other)); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_storage &other) noexcept { + using std::swap; + swap(placeholder, other.placeholder); + base_type::swap(other); + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + } + + /** + * @brief Returns an empty tuple. + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + return std::tuple{}; + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + entity_type generate() { + const auto len = base_type::free_list(); + const auto entt = (len == base_type::size()) ? next() : base_type::data()[len]; + return *base_type::try_emplace(entt, true); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * + * If the requested identifier isn't in use, the suggested one is used. + * Otherwise, a new identifier is returned. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type generate(const entity_type hint) { + if(hint != null && hint != tombstone) { + if(const auto curr = traits_type::construct(traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone || !(base_type::index(curr) < base_type::free_list())) { + return *base_type::try_emplace(hint, true); + } + } + + return generate(); + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template + void generate(It first, It last) { + for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) { + *first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true); + } + + for(; first != last; ++first) { + *first = *base_type::try_emplace(next(), true); + } + } + + /** + * @brief Updates a given identifier. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + (std::forward(func)(), ...); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return std::as_const(*this).each(); + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + const auto it = base_type::cend(); + const auto offset = static_cast(base_type::free_list()); + return const_iterable{it - offset, it}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return std::as_const(*this).reach(); + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + const auto it = base_type::crbegin(); + const auto offset = static_cast(base_type::free_list()); + return const_reverse_iterable{it, it + offset}; + } + + /** + * @brief Sets the starting identifier for generation. + * + * The version is ignored, regardless of the value. + * + * @param hint A valid identifier. + */ + void start_from(const entity_type hint) { + placeholder = static_cast(traits_type::to_entity(hint)); + } + +private: + size_type placeholder{}; +}; + +} // namespace entt + +#endif + +// #include "view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +// NOLINTNEXTLINE(misc-redundant-expression) +static constexpr bool tombstone_check_v = ((sizeof...(Type) == 1u) && ... && (Type::storage_policy == deletion_policy::in_place)); + +template +const Type *view_placeholder() { + static_assert(std::is_same_v>, Type>, "Unexpected type"); + static const Type placeholder{}; + return &placeholder; +} + +template +[[nodiscard]] bool all_of(It first, const It last, const Entity entt) noexcept { + for(; (first != last) && (*first)->contains(entt); ++first) {} + return first == last; +} + +template +[[nodiscard]] bool none_of(It first, const It last, const Entity entt) noexcept { + for(; (first != last) && !(*first)->contains(entt); ++first) {} + return first == last; +} + +template +[[nodiscard]] bool fully_initialized(It first, const It last, const std::remove_pointer_t::value_type> *placeholder) noexcept { + for(; (first != last) && *first != placeholder; ++first) {} + return first == last; +} + +template +[[nodiscard]] Result view_pack(const View &view, const Other &other, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence) { + Result elem{}; + // friend-initialization, avoid multiple calls to refresh + elem.pools = {view.template storage()..., other.template storage()...}; + auto filter_or_placeholder = [placeholder = elem.placeholder](auto *value) { return (value == nullptr) ? placeholder : value; }; + elem.filter = {filter_or_placeholder(view.template storage())..., filter_or_placeholder(other.template storage())...}; + elem.refresh(); + return elem; +} + +template +class view_iterator final { + template + friend class extended_view_iterator; + + using iterator_type = typename Type::const_iterator; + using iterator_traits = std::iterator_traits; + + [[nodiscard]] bool valid(const typename iterator_traits::value_type entt) const noexcept { + return (!Checked || (entt != tombstone)) + && ((Get == 1u) || (internal::all_of(pools.begin(), pools.begin() + index, entt) && internal::all_of(pools.begin() + index + 1, pools.end(), entt))) + && ((Exclude == 0u) || internal::none_of(filter.begin(), filter.end(), entt)); + } + + void seek_next() { + for(constexpr iterator_type sentinel{}; it != sentinel && !valid(*it); ++it) {} + } + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::forward_iterator_tag; + + constexpr view_iterator() noexcept + : it{}, + pools{}, + filter{}, + index{} {} + + view_iterator(iterator_type first, std::array value, std::array excl, const std::size_t idx) noexcept + : it{first}, + pools{value}, + filter{excl}, + index{static_cast(idx)} { + ENTT_ASSERT((Get != 1u) || (Exclude != 0u) || pools[0u]->policy() == deletion_policy::in_place, "Non in-place storage view iterator"); + seek_next(); + } + + view_iterator &operator++() noexcept { + ++it; + seek_next(); + return *this; + } + + view_iterator operator++(int) noexcept { + const view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return &*it; + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; + +private: + iterator_type it; + std::array pools; + std::array filter; + difference_type index; +}; + +template +[[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +class extended_view_iterator final { + template + [[nodiscard]] auto dereference(std::index_sequence) const noexcept { + return std::tuple_cat(std::make_tuple(*it), static_cast(const_cast *>(std::get(it.pools)))->get_as_tuple(*it)...); + } + +public: + using iterator_type = It; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_view_iterator() + : it{} {} + + extended_view_iterator(iterator_type from) + : it{from} {} + + extended_view_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_view_iterator operator++(int) noexcept { + const extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return dereference(std::index_sequence_for{}); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief View implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + * + * @b Important + * + * View iterators aren't invalidated if: + * + * * New elements are added to the storage iterated by the view. + * * The entity currently returned is modified (for example, elements are added + * or removed from it). + * * The entity currently returned is destroyed. + * + * In all other cases, modifying the storage iterated by a view in any way can + * invalidate all iterators. + */ +template +class basic_view; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Checked True to enable the tombstone check, false otherwise. + * @tparam Get Number of storage iterated by the view. + * @tparam Exclude Number of storage used to filter the view. + */ +template +class basic_common_view { + static_assert(std::is_same_v>, Type>, "Unexpected type"); + + template + friend Return internal::view_pack(const View &, const Other &, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence); + + [[nodiscard]] auto offset() const noexcept { + ENTT_ASSERT(index != Get, "Invalid view"); + return (pools[index]->policy() == deletion_policy::swap_only) ? pools[index]->free_list() : pools[index]->size(); + } + + void unchecked_refresh() noexcept { + index = 0u; + + if constexpr(Get > 1u) { + for(size_type pos{1u}; pos < Get; ++pos) { + if(pools[pos]->size() < pools[index]->size()) { + index = pos; + } + } + } + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_common_view() noexcept { + for(size_type pos{}, last = filter.size(); pos < last; ++pos) { + filter[pos] = placeholder; + } + } + + basic_common_view(std::array value, std::array excl) noexcept + : pools{value}, + filter{excl}, + index{Get} { + unchecked_refresh(); + } + + [[nodiscard]] const Type *pool_at(const std::size_t pos) const noexcept { + return pools[pos]; + } + + void pool_at(const std::size_t pos, const Type *elem) noexcept { + ENTT_ASSERT(elem != nullptr, "Unexpected element"); + pools[pos] = elem; + refresh(); + } + + [[nodiscard]] const Type *filter_at(const std::size_t pos) const noexcept { + return (filter[pos] == placeholder) ? nullptr : filter[pos]; + } + + void filter_at(const std::size_t pos, const Type *elem) noexcept { + ENTT_ASSERT(elem != nullptr, "Unexpected element"); + filter[pos] = elem; + } + + [[nodiscard]] bool none_of(const typename Type::entity_type entt) const noexcept { + return internal::none_of(filter.begin(), filter.end(), entt); + } + + void use(const std::size_t pos) noexcept { + index = (index != Get) ? pos : Get; + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Forward iterator type. */ + using iterator = internal::view_iterator; + + /*! @brief Updates the internal leading view if required. */ + void refresh() noexcept { + size_type pos = static_cast(index != Get) * Get; + for(; pos < Get && pools[pos] != nullptr; ++pos) {} + + if(pos == Get) { + unchecked_refresh(); + } + } + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return (index != Get) ? pools[index] : nullptr; + } + + /** + * @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 noexcept { + return (index != Get) ? offset() : size_type{}; + } + + /** + * @brief Returns an iterator 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 noexcept { + return (index != Get) ? iterator{pools[index]->end() - static_cast(offset()), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return (index != Get) ? iterator{pools[index]->end(), pools, filter, index} : iterator{}; + } + + /** + * @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 noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + if(index != Get) { + auto it = pools[index]->rbegin(); + const auto last = it + static_cast(offset()); + for(const auto idx = static_cast(index); it != last && !(internal::all_of(pools.begin(), pools.begin() + idx, *it) && internal::all_of(pools.begin() + idx + 1, pools.end(), *it) && internal::none_of(filter.begin(), filter.end(), *it)); ++it) {} + return it == last ? null : *it; + } + + return null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return contains(entt) ? iterator{pools[index]->find(entt), pools, filter, index} : end(); + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (index != Get) && internal::fully_initialized(filter.begin(), filter.end(), placeholder); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return (index != Get) + && internal::all_of(pools.begin(), pools.end(), entt) + && internal::none_of(filter.begin(), filter.end(), entt) + && pools[index]->index(entt) < offset(); + } + +private: + std::array pools{}; + std::array filter{}; + const common_type *placeholder{internal::view_placeholder()}; + size_type index{Get}; +}; + +/** + * @brief General purpose view. + * + * This view visits all entities that are at least in the given storage. During + * initialization, it also looks at the number of elements available for each + * storage and uses the smallest set in order to get a performance boost. + * + * @sa basic_view + * + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template +class basic_view, exclude_t, std::enable_if_t<(sizeof...(Get) != 0u)>> + : public basic_common_view, internal::tombstone_check_v, sizeof...(Get), sizeof...(Exclude)> { + using base_type = basic_common_view, internal::tombstone_check_v, sizeof...(Get), sizeof...(Exclude)>; + + template + using element_at = type_list_element_t>; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + [[nodiscard]] auto get(const typename base_type::entity_type entt, std::index_sequence) const noexcept { + return std::tuple_cat(storage()->get_as_tuple(entt)...); + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Curr == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return storage()->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func &func, std::index_sequence) const { + for(const auto curr: storage()->each()) { + if(const auto entt = std::get<0>(curr); (!internal::tombstone_check_v || (entt != tombstone)) && ((Curr == Index || base_type::pool_at(Index)->contains(entt)) && ...) && base_type::none_of(entt)) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(curr)...)); + } + } + } + } + + template + void pick_and_each(Func &func, std::index_sequence seq) const { + if(const auto *view = base_type::handle(); view != nullptr) { + ((view == base_type::pool_at(Index) ? each(func, seq) : void()), ...); + } + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Forward iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Iterable view type. */ + using iterable = iterable_adaptor>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(Get &...value, Exclude &...excl) noexcept + : base_type{{&value...}, {&excl...}} { + } + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(std::tuple value, std::tuple excl = {}) noexcept + : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} + + /** + * @brief Forces a view to use a given element to drive iterations + * @tparam Type Type of element to use to drive iterations. + */ + template + void use() noexcept { + use>(); + } + + /** + * @brief Forces a view to use a given element to drive iterations + * @tparam Index Index of the element to use to drive iterations. + */ + template + void use() noexcept { + base_type::use(Index); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + if constexpr(Index < sizeof...(Get)) { + return static_cast *>(const_cast> *>(base_type::pool_at(Index))); + } else { + return static_cast *>(const_cast> *>(base_type::filter_at(Index - sizeof...(Get)))); + } + } + + /** + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + storage>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + static_assert(std::is_convertible_v &>, "Unexpected type"); + + if constexpr(Index < sizeof...(Get)) { + base_type::pool_at(Index, &elem); + } else { + base_type::filter_at(Index - sizeof...(Get), &elem); + } + } + + /** + * @brief Returns the elements assigned to the given entity. + * @param entt A valid identifier. + * @return The elements assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return get(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Type Type of the element to get. + * @tparam Other Other types of elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Index Indexes of the elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return get(entt, std::index_sequence_for{}); + } else if constexpr(sizeof...(Index) == 1) { + return (storage()->get(entt), ...); + } else { + return std::tuple_cat(storage()->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and elements and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + pick_and_each(func, std::index_sequence_for{}); + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty elements. The _constness_ of the + * elements is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + return iterable{base_type::begin(), base_type::end()}; + } + + /** + * @brief Combines a view and a storage in _more specific_ view. + * @tparam OGet Type of storage to combine the view with. + * @param other The storage for the type to combine the view with. + * @return A more specific view. + */ + template + [[nodiscard]] std::enable_if_t, basic_view, exclude_t>> operator|(OGet &other) const noexcept { + return *this | basic_view, exclude_t<>>{other}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Element list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Policy Storage policy. + */ +template +class basic_storage_view { + static_assert(std::is_same_v>, Type>, "Unexpected type"); + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_storage_view() noexcept = default; + + basic_storage_view(const Type *value) noexcept + : leading{value} { + ENTT_ASSERT(leading->policy() == Policy, "Unexpected storage policy"); + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename common_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Random access iterator type. */ + using iterator = std::conditional_t, typename common_type::iterator>; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::conditional_t; + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Returns the number of entities that have the given element. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return Number of entities that have the given element. + */ + template + [[nodiscard]] std::enable_if_t size() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->size() : size_type{}; + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return leading ? leading->free_list() : size_type{}; + } + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return Estimated number of entities iterated by the view. + */ + template + [[nodiscard]] std::enable_if_t size_hint() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Checks whether a view is empty. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return True if the view is empty, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t empty() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return !leading || leading->empty(); + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return !leading || (leading->free_list() == 0u); + } + } + + /** + * @brief Returns an iterator 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 noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->begin() : iterator{}; + } else if constexpr(Policy == deletion_policy::swap_only) { + return leading ? (leading->end() - static_cast(leading->free_list())) : iterator{}; + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + return leading ? iterator{leading->begin(), {leading}, {}, 0u} : iterator{}; + } + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) { + return leading ? leading->end() : iterator{}; + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + return leading ? iterator{leading->end(), {leading}, {}, 0u} : iterator{}; + } + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * If the view is empty, the returned iterator will be equal to `rend()`. + * + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return An iterator to the first entity of the reversed view. + */ + template + [[nodiscard]] std::enable_if_t rbegin() const noexcept { + return leading ? leading->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + template + [[nodiscard]] std::enable_if_t rend() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->rend() : reverse_iterator{}; + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return leading ? (leading->rbegin() + static_cast(leading->free_list())) : reverse_iterator{}; + } + } + + /** + * @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 noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return empty() ? null : *leading->begin(); + } else if constexpr(Policy == deletion_policy::swap_only) { + return empty() ? null : *(leading->end() - static_cast(leading->free_list())); + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + const auto it = begin(); + return (it == end()) ? null : *it; + } + } + + /** + * @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 noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) { + return empty() ? null : *leading->rbegin(); + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + + if(leading) { + auto it = leading->rbegin(); + const auto last = leading->rend(); + for(; (it != last) && (*it == tombstone); ++it) {} + return it == last ? null : *it; + } + + return null; + } + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->find(entt) : iterator{}; + } else if constexpr(Policy == deletion_policy::swap_only) { + const auto it = leading ? leading->find(entt) : iterator{}; + return leading && (static_cast(it.index()) < leading->free_list()) ? it : iterator{}; + } else { + return leading ? iterator{leading->find(entt), {leading}, {}, 0u} : iterator{}; + } + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (leading != nullptr); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::in_place) { + return leading && leading->contains(entt); + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return leading && leading->contains(entt) && (leading->index(entt) < leading->free_list()); + } + } + +private: + const common_type *leading{}; +}; + +/** + * @brief Storage view specialization. + * + * This specialization offers a boost in terms of performance. It can access the + * underlying data structure directly and avoid superfluous checks. + * + * @sa basic_view + * + * @tparam Get Type of storage iterated by the view. + */ +template +class basic_view, exclude_t<>> + : public basic_storage_view { + using base_type = basic_storage_view; + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Random access iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = typename base_type::reverse_iterator; + /*! @brief Iterable view type. */ + using iterable = std::conditional_t>, decltype(std::declval().each())>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(Get &value) noexcept + : base_type{&value} { + } + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(std::tuple value, std::tuple<> = {}) noexcept + : basic_view{std::get<0>(value)} {} + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(std::is_same_v, typename Get::element_type>, "Invalid element type"); + return storage<0>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(Index == 0u, "Index out of bounds"); + return static_cast(const_cast *>(base_type::handle())); + } + + /** + * @brief Assigns a storage to a view. + * @param elem A storage to assign to the view. + */ + void storage(Get &elem) noexcept { + storage<0>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Get &elem) noexcept { + static_assert(Index == 0u, "Index out of bounds"); + *this = basic_view{elem}; + } + + /** + * @brief Returns a pointer to the underlying storage. + * @return A pointer to the underlying storage. + */ + [[nodiscard]] Get *operator->() const noexcept { + return storage(); + } + + /** + * @brief Returns the element assigned to the given entity. + * @param entt A valid identifier. + * @return The element assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return storage()->get(entt); + } + + /** + * @brief Returns the element assigned to the given entity. + * @tparam Elem Type of the element to get. + * @param entt A valid identifier. + * @return The element assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + static_assert(std::is_same_v, typename Get::element_type>, "Invalid element type"); + return get<0>(entt); + } + + /** + * @brief Returns the element assigned to the given entity. + * @tparam Index Index of the element to get. + * @param entt A valid identifier. + * @return The element assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return storage()->get_as_tuple(entt); + } else { + return storage()->get(entt); + } + } + + /** + * @brief Iterates entities and elements and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &); + * void(typename Type &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + for(const auto pack: each()) { + std::apply(func, pack); + } + } else if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) { + if constexpr(std::is_void_v) { + for(size_type pos = base_type::size(); pos; --pos) { + func(); + } + } else { + if(const auto len = static_cast(base_type::size()); len != 0) { + for(auto last = storage()->end(), first = last - len; first != last; ++first) { + func(*first); + } + } + } + } else { + static_assert(Get::storage_policy == deletion_policy::in_place, "Unexpected storage policy"); + + for(const auto pack: each()) { + std::apply([&func](const auto, auto &&...elem) { func(std::forward(elem)...); }, pack); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its element if it's a non-empty one. The _constness_ of + * the element is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) { + return base_type::handle() ? storage()->each() : iterable{}; + } else { + static_assert(Get::storage_policy == deletion_policy::in_place, "Unexpected storage policy"); + return iterable{base_type::begin(), base_type::end()}; + } + } + + /** + * @brief Combines a view and a storage in _more specific_ view. + * @tparam OGet Type of storage to combine the view with. + * @param other The storage for the type to combine the view with. + * @return A more specific view. + */ + template + [[nodiscard]] std::enable_if_t, basic_view, exclude_t<>>> operator|(OGet &other) const noexcept { + return *this | basic_view, exclude_t<>>{other}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Element list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for<>{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Type &...storage) -> basic_view, exclude_t<>>; + +/** + * @brief Deduction guide. + * @tparam Get Types of elements iterated by the view. + * @tparam Exclude Types of elements used to filter the view. + */ +template +basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Registry Basic registry type. + */ +template +class as_view { + template + [[nodiscard]] auto dispatch(get_t, exclude_t) const { + return reg->template view...>(exclude_t...>{}); + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) noexcept + : reg{&source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Get Type of storage used to construct the view. + * @tparam Exclude Types of storage used to filter the view. + * @return A newly created view. + */ + template + operator basic_view() const { + return dispatch(Get{}, Exclude{}); + } + +private: + registry_type *reg; +}; + +/** + * @brief Converts a registry to a group. + * @tparam Registry Basic registry type. + */ +template +class as_group { + template + [[nodiscard]] auto dispatch(owned_t, get_t, exclude_t) const { + if constexpr(std::is_const_v) { + return reg->template group_if_exists(get_t{}, exclude_t{}); + } else { + return reg->template group...>(get_t...>{}, exclude_t...>{}); + } + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) noexcept + : reg{&source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Owned Types of _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + * @return A newly created group. + */ + template + operator basic_group() const { + return dispatch(Owned{}, Get{}, Exclude{}); + } + +private: + registry_type *reg; +}; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on an element of the given type. + * @tparam Registry Basic registry type. + * @param reg A registry that contains the given entity and its elements. + * @param entt Entity from which to get the element. + */ +template>> +void invoke(Registry ®, const typename Registry::entity_type entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + (reg.template get>(entt).*Member)(reg, entt); +} + +/** + * @brief Returns the entity associated with a given element. + * + * @warning + * Currently, this function only works correctly with the default storage as it + * makes assumptions about how the elements are laid out. + * + * @tparam Args Storage type template parameters. + * @param storage A storage that contains the given element. + * @param instance A valid element instance. + * @return The entity associated with the given element. + */ +template +typename basic_storage::entity_type to_entity(const basic_storage &storage, const typename basic_storage::value_type &instance) { + using traits_type = component_traits::value_type, typename basic_storage::entity_type>; + static_assert(traits_type::page_size != 0u, "Unexpected page size"); + const auto *page = storage.raw(); + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(std::size_t pos{}, count = storage.size(); pos < count; pos += traits_type::page_size, ++page) { + if(const auto dist = (std::addressof(instance) - *page); dist >= 0 && dist < static_cast(traits_type::page_size)) { + return *(static_cast::base_type &>(storage).rbegin() + static_cast(pos) + dist); + } + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + return null; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct sigh_helper; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + */ +template +struct sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + */ + sigh_helper(registry_type &ref) + : bucket{&ref} {} + + /** + * @brief Binds a properly initialized helper to a given signal type. + * @tparam Type Type of signal to bind the helper to. + * @param id Optional name for the underlying storage to use. + * @return A helper for a given registry and signal type. + */ + template + auto with(const id_type id = type_hash::value()) noexcept { + return sigh_helper{*bucket, id}; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type ®istry() noexcept { + return *bucket; + } + +private: + registry_type *bucket; +}; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + * @tparam Type Type of signal to connect listeners to. + */ +template +struct sigh_helper final: sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + * @param id Optional name for the underlying storage to use. + */ + sigh_helper(registry_type &ref, const id_type id = type_hash::value()) + : sigh_helper{ref}, + name{id} {} + + /** + * @brief Forwards the call to `on_construct` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_construct(Args &&...args) { + this->registry().template on_construct(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_update` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_update(Args &&...args) { + this->registry().template on_update(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_destroy` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_destroy(Args &&...args) { + this->registry().template on_destroy(name).template connect(std::forward(args)...); + return *this; + } + +private: + id_type name; +}; + +/** + * @brief Deduction guide. + * @tparam Registry Basic registry type. + */ +template +sigh_helper(Registry &) -> sigh_helper; + +} // namespace entt + +#endif + +// #include "entity/mixin.hpp" +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP + +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../core/type_info.hpp" + +// #include "../signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include + +namespace entt { + +template +class delegate; + +template> +class basic_dispatcher; + +template> +class emitter; + +class connection; + +struct scoped_connection; + +template +class sink; + +template> +class sigh; + +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; + +/*! @brief Disambiguation tag for constructors and the like. */ +template +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; + +/** + * @brief Constant of type connect_arg_t used to disambiguate calls. + * @tparam Candidate Element to connect (likely a free or member function). + */ +template +inline constexpr connect_arg_t connect_arg{}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template>> +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 { + using return_type = std::remove_const_t; + using delegate_type = return_type(const void *, Args...); + + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> return_type { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + 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() noexcept = default; + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) 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) 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() noexcept { + instance = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> return_type { + 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) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> return_type { + 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) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> return_type { + 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) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); + return fn(instance, 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 noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance{}; + delegate_type *fn{}; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) 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; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink>; + + /*! @brief Default constructor. */ + sigh() noexcept(noexcept(allocator_type{})) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) + : calls{std::move(other.calls), allocator} {} + + /*! @brief Default destructor. */ + ~sigh() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept { + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const 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 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 pos = calls.size(); pos; --pos) { + calls[pos - 1u](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 pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](args...)); + } + } + } + } + +private: + container_type calls; +}; + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const 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 Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection &operator=(const scoped_connection &) = delete; + + /** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection &operator=(connection other) { + conn = 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 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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + using difference_type = typename signal_type::container_type::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(); + } + + template + void disconnect_if(Func callback) { + auto &ref = signal_or_assert(); + + for(auto pos = ref.calls.size(); pos; --pos) { + if(auto &elem = ref.calls[pos - 1u]; callback(elem)) { + elem = std::move(ref.calls.back()); + ref.calls.pop_back(); + } + } + } + + [[nodiscard]] auto &signal_or_assert() const noexcept { + ENTT_ASSERT(signal != nullptr, "Invalid pointer to signal"); + return *signal; + } + +public: + /*! @brief Constructs an invalid sink. */ + sink() noexcept + : signal{} {} + + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) noexcept + : 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 noexcept { + return signal_or_assert().calls.empty(); + } + + /** + * @brief Connects a free function or an unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate_type call{}; + call.template connect(); + signal_or_assert().calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return {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.
+ * 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 reference that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &value_or_instance) { + disconnect(value_or_instance); + + delegate_type call{}; + call.template connect(value_or_instance); + signal_or_assert().calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return {conn, signal}; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * @sa connect(Type &) + * + * @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 pointer that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type *value_or_instance) { + disconnect(value_or_instance); + + delegate_type call{}; + call.template connect(value_or_instance); + signal_or_assert().calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return {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() { + delegate_type call{}; + call.template connect(); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects a free function with payload or a bound member from 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.
+ * 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 disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * + * @sa disconnect(Type &) + * + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + ENTT_ASSERT(value_or_instance != nullptr, "Invalid value or instance"); + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal_or_assert().calls.clear(); + } + + /** + * @brief Returns true if a sink is correctly initialized, false otherwise. + * @return True if a sink is correctly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return signal != nullptr; + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler type of a sink directly from the + * signal it refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +sink(sigh &) -> sink>; + +} // namespace entt + +#endif + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_on_construct final: std::false_type {}; + +template +struct has_on_construct(), std::declval().create()))>> + : std::true_type {}; + +template +struct has_on_update final: std::false_type {}; + +template +struct has_on_update(), std::declval().create()))>> + : std::true_type {}; + +template +struct has_on_destroy final: std::false_type {}; + +template +struct has_on_destroy(), std::declval().create()))>> + : std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Mixin type used to add signal support to storage types. + * + * The function type of a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * This applies to all signals made available. + * + * @tparam Type Underlying storage type. + * @tparam Registry Basic registry type. + */ +template +class basic_sigh_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using basic_registry_type = basic_registry; + using sigh_type = sigh; + using underlying_iterator = typename underlying_type::base_type::basic_iterator; + + static_assert(std::is_base_of_v, "Invalid registry type"); + + [[nodiscard]] auto &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + +private: + void pop(underlying_iterator first, underlying_iterator last) final { + if(auto ® = owner_or_assert(); destruction.empty()) { + underlying_type::pop(first, last); + } else { + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(reg, entt); + const auto it = underlying_type::find(entt); + underlying_type::pop(it, it + 1u); + } + } + } + + void pop_all() final { + if(auto ® = owner_or_assert(); !destruction.empty()) { + if constexpr(std::is_same_v) { + for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) { + destruction.publish(reg, underlying_type::base_type::operator[](pos)); + } + } else { + for(auto entt: static_cast(*this)) { + if constexpr(underlying_type::storage_policy == deletion_policy::in_place) { + if(entt != tombstone) { + destruction.publish(reg, entt); + } + } else { + destruction.publish(reg, entt); + } + } + } + } + + underlying_type::pop_all(); + } + + underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { + const auto it = underlying_type::try_emplace(entt, force_back, value); + + if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { + construction.publish(reg, *it); + } + + return it; + } + + void bind_any(any value) noexcept final { + owner = any_cast(&value); + + if constexpr(!std::is_same_v) { + if(owner == nullptr) { + owner = any_cast(&value); + } + } + + underlying_type::bind_any(std::move(value)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_sigh_mixin() + : basic_sigh_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sigh_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} { + if constexpr(internal::has_on_construct::value) { + sink{construction}.template connect<&underlying_type::element_type::on_construct>(); + } + + if constexpr(internal::has_on_update::value) { + sink{update}.template connect<&underlying_type::element_type::on_update>(); + } + + if constexpr(internal::has_on_destroy::value) { + sink{destruction}.template connect<&underlying_type::element_type::on_destroy>(); + } + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_sigh_mixin(const basic_sigh_mixin &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sigh_mixin(basic_sigh_mixin &&other) noexcept + : underlying_type{static_cast(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) + : underlying_type{static_cast(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} + + /*! @brief Default destructor. */ + ~basic_sigh_mixin() override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This mixin. + */ + basic_sigh_mixin &operator=(const basic_sigh_mixin &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This mixin. + */ + basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept { + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_sigh_mixin &other) noexcept { + using std::swap; + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); + underlying_type::swap(other); + } + + /** + * @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.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() noexcept { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; + } + + /** + * @brief Checks if a mixin refers to a valid registry. + * @return True if the mixin refers to a valid registry, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (owner != nullptr); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] const registry_type ®istry() const noexcept { + return owner_or_assert(); + } + + /*! @copydoc registry */ + [[nodiscard]] registry_type ®istry() noexcept { + return owner_or_assert(); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + auto generate() { + const auto entt = underlying_type::generate(); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type generate(const entity_type hint) { + const auto entt = underlying_type::generate(hint); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template + void generate(It first, It last) { + underlying_type::generate(first, last); + + if(auto ® = owner_or_assert(); !construction.empty()) { + for(; first != last; ++first) { + construction.publish(reg, *first); + } + } + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * @tparam Args Types of arguments to forward to the underlying storage. + * @param entt A valid identifier. + * @param args Parameters to forward to the underlying storage. + * @return A reference to the newly created object. + */ + template + decltype(auto) emplace(const entity_type entt, Args &&...args) { + underlying_type::emplace(entt, std::forward(args)...); + construction.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + underlying_type::patch(entt, std::forward(func)...); + update.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * @tparam It Type of input iterator. + * @tparam Args Types of arguments to forward to the underlying storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param args Parameters to use to forward to the underlying storage. + */ + template + void insert(It first, It last, Args &&...args) { + auto from = underlying_type::size(); + underlying_type::insert(first, last, std::forward(args)...); + + if(auto ® = owner_or_assert(); !construction.empty()) { + // fine as long as insert passes force_back true to try_emplace + for(const auto to = underlying_type::size(); from != to; ++from) { + construction.publish(reg, underlying_type::operator[](from)); + } + } + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; + +/** + * @brief Mixin type used to add _reactive_ support to storage types. + * @tparam Type Underlying storage type. + * @tparam Registry Basic registry type. + */ +template +class basic_reactive_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using alloc_traits = std::allocator_traits; + using basic_registry_type = basic_registry; + using container_type = std::vector>; + + static_assert(std::is_base_of_v, "Invalid registry type"); + + [[nodiscard]] auto &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + + void emplace_element(const Registry &, typename underlying_type::entity_type entity) { + if(!underlying_type::contains(entity)) { + underlying_type::emplace(entity); + } + } + +private: + void bind_any(any value) noexcept final { + owner = any_cast(&value); + + if constexpr(!std::is_same_v) { + if(owner == nullptr) { + owner = any_cast(&value); + } + } + + underlying_type::bind_any(std::move(value)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_reactive_mixin() + : basic_reactive_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_reactive_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + conn{allocator} { + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_reactive_mixin(const basic_reactive_mixin &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_reactive_mixin(basic_reactive_mixin &&other) noexcept + : underlying_type{static_cast(other)}, + owner{other.owner}, + conn{std::move(other.conn)} { + } + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator) + : underlying_type{static_cast(other), allocator}, + owner{other.owner}, + conn{std::move(other.conn), allocator} { + } + + /*! @brief Default destructor. */ + ~basic_reactive_mixin() override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This mixin. + */ + basic_reactive_mixin &operator=(const basic_reactive_mixin &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This mixin. + */ + basic_reactive_mixin &operator=(basic_reactive_mixin &&other) noexcept { + underlying_type::swap(other); + return *this; + } + + /** + * @brief Makes storage _react_ to creation of objects of the given type. + * @tparam Clazz Type of element to _react_ to. + * @tparam Candidate Function to use to _react_ to the event. + * @param id Optional name used to map the storage within the registry. + * @return This mixin. + */ + template + basic_reactive_mixin &on_construct(const id_type id = type_hash::value()) { + auto curr = owner_or_assert().template storage(id).on_construct().template connect(*this); + conn.push_back(std::move(curr)); + return *this; + } + + /** + * @brief Makes storage _react_ to update of objects of the given type. + * @tparam Clazz Type of element to _react_ to. + * @tparam Candidate Function to use to _react_ to the event. + * @param id Optional name used to map the storage within the registry. + * @return This mixin. + */ + template + basic_reactive_mixin &on_update(const id_type id = type_hash::value()) { + auto curr = owner_or_assert().template storage(id).on_update().template connect(*this); + conn.push_back(std::move(curr)); + return *this; + } + + /** + * @brief Makes storage _react_ to destruction of objects of the given type. + * @tparam Clazz Type of element to _react_ to. + * @tparam Candidate Function to use to _react_ to the event. + * @param id Optional name used to map the storage within the registry. + * @return This mixin. + */ + template + basic_reactive_mixin &on_destroy(const id_type id = type_hash::value()) { + auto curr = owner_or_assert().template storage(id).on_destroy().template connect(*this); + conn.push_back(std::move(curr)); + return *this; + } + + /** + * @brief Checks if a mixin refers to a valid registry. + * @return True if the mixin refers to a valid registry, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (owner != nullptr); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] const registry_type ®istry() const noexcept { + return owner_or_assert(); + } + + /*! @copydoc registry */ + [[nodiscard]] registry_type ®istry() noexcept { + return owner_or_assert(); + } + + /** + * @brief Returns a view that is filtered by the underlying storage. + * @tparam Get Types of elements used to construct the view. + * @tparam Exclude Types of elements used to filter the view. + * @return A newly created view. + */ + template + [[nodiscard]] basic_view...>, exclude_t...>> + view(exclude_t = exclude_t{}) const { + const owner_type &parent = owner_or_assert(); + basic_view...>, exclude_t...>> elem{}; + [&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage>()..., parent.template storage>()..., this); + return elem; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view...>, exclude_t...>> + view(exclude_t = exclude_t{}) { + std::conditional_t<((std::is_const_v && ...) && (std::is_const_v && ...)), const owner_type, owner_type> &parent = owner_or_assert(); + return {*this, parent.template storage>()..., parent.template storage>()...}; + } + + /*! @brief Releases all connections to the underlying registry, if any. */ + void reset() { + for(auto &&curr: conn) { + curr.release(); + } + + conn.clear(); + } + +private: + basic_registry_type *owner; + container_type conn; +}; + +} // namespace entt + +#endif + +// #include "entity/organizer.hpp" +#ifndef ENTT_ENTITY_ORGANIZER_HPP +#define ENTT_ENTITY_ORGANIZER_HPP + +#include +#include +#include +#include +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "../graph/adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template> +class adjacency_matrix; + +template> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + + void find_next() noexcept { + for(; pos != last && !it[static_cast(pos)]; pos += offset) {} + } + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept = default; + + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + find_next(); + } + + constexpr edge_iterator &operator++() noexcept { + pos += offset; + find_next(); + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + const edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it{}; + size_type vert{}; + size_type pos{}; + size_type last{}; + size_type offset{}; +}; + +template +[[nodiscard]] constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out-edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In-edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /*! @brief Default copy constructor. */ + adjacency_matrix(const adjacency_matrix &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /*! @brief Default move constructor. */ + adjacency_matrix(adjacency_matrix &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{other.vert} {} + + /*! @brief Default destructor. */ + ~adjacency_matrix() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) noexcept { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Returns true if an adjacency matrix is empty, false otherwise. + * + * @warning + * Potentially expensive, try to avoid it on hot paths. + * + * @return True if the adjacency matrix is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + const auto iterable = edges(); + return (iterable.begin() == iterable.end()); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out-edges of a vertex. + * @param vertex The vertex of which to return all out-edges. + * @return An iterable object to visit all out-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in-edges of a vertex. + * @param vertex The vertex of which to return all in-edges. + * @return An iterable object to visit all in-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "../graph/flow.hpp" +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator> +class dense_set; + +template +class basic_table; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Element types. + */ +template +using table = basic_table...>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits::max)(); + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + const dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + const dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept = default; + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return (offset = it[static_cast(offset)].next), *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + const dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + const auto idx = static_cast(offset); + return {it[idx].element.first, it[idx].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_map_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_map() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses a given element with bounds checking. + * @tparam Other Type of the key of an element to find. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/bit.hpp" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_set_placeholder_position = (std::numeric_limits::max)(); + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + const dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + const dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept = default; + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[static_cast(offset)].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + const dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[static_cast(offset)].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_set_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_set_placeholder_position; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) { + if(packed.second()(packed.first()[offset].second, value)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) { + if(packed.second()(packed.first()[offset].second, value)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_set() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array 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 noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + + void find_next() noexcept { + for(; pos != last && !it[static_cast(pos)]; pos += offset) {} + } + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept = default; + + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + find_next(); + } + + constexpr edge_iterator &operator++() noexcept { + pos += offset; + find_next(); + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + const edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it{}; + size_type vert{}; + size_type pos{}; + size_type last{}; + size_type offset{}; +}; + +template +[[nodiscard]] constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out-edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In-edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /*! @brief Default copy constructor. */ + adjacency_matrix(const adjacency_matrix &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /*! @brief Default move constructor. */ + adjacency_matrix(adjacency_matrix &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{other.vert} {} + + /*! @brief Default destructor. */ + ~adjacency_matrix() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) noexcept { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Returns true if an adjacency matrix is empty, false otherwise. + * + * @warning + * Potentially expensive, try to avoid it on hot paths. + * + * @return True if the adjacency matrix is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + const auto iterable = edges(); + return (iterable.begin() == iterable.end()); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out-edges of a vertex. + * @param vertex The vertex of which to return all out-edges. + * @return An iterable object to visit all out-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in-edges of a vertex. + * @param vertex The vertex of which to return all in-edges. + * @return An iterable object to visit all in-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class for creating task graphs. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_flow { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; + using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; + using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; + using adjacency_matrix_type = adjacency_matrix>; + + void emplace(const id_type res, const bool is_rw) { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + + if(!deps.contains(res) && sync_on != vertices.size()) { + deps[res].emplace_back(sync_on, true); + } + + deps[res].emplace_back(index.first(), is_rw); + } + + void setup_graph(adjacency_matrix_type &matrix) const { + for(const auto &elem: deps) { + const auto last = elem.second.cend(); + auto it = elem.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + matrix.insert(curr->first, it->first); + } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + matrix.insert(it->first, next->first); + } + } else { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + } + } + } + } else { + // ro item (first iteration only) + if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(it->first, next->first); + } + } else { + it = last; + } + } + } + } + } + + void transitive_closure(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { + matrix.insert(vi, vj); + } + } + } + } + } + + void transitive_reduction(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(std::size_t vk{}; vk < length; ++vk) { + if(matrix.contains(vj, vk)) { + matrix.erase(vi, vk); + } + } + } + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable task list. */ + using iterable = iterable_adaptor; + /*! @brief Adjacency matrix type. */ + using graph_type = adjacency_matrix_type; + + /*! @brief Default constructor. */ + basic_flow() + : basic_flow{allocator_type{}} {} + + /** + * @brief Constructs a flow builder with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_flow(const allocator_type &allocator) + : index{0u, allocator}, + vertices{allocator}, + deps{allocator} {} + + /*! @brief Default copy constructor. */ + basic_flow(const basic_flow &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_flow(const basic_flow &other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{other.vertices, allocator}, + deps{other.deps, allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default move constructor. */ + basic_flow(basic_flow &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_flow(basic_flow &&other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{std::move(other.vertices), allocator}, + deps{std::move(other.deps), allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default destructor. */ + ~basic_flow() = default; + + /** + * @brief Default copy assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(const basic_flow &) = default; + + /** + * @brief Default move assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(basic_flow &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given flow builder. + * @param other Flow builder to exchange the content with. + */ + void swap(basic_flow &other) noexcept { + using std::swap; + std::swap(index, other.index); + std::swap(vertices, other.vertices); + std::swap(deps, other.deps); + std::swap(sync_on, other.sync_on); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{index.second()}; + } + + /** + * @brief Returns the identifier at specified location. + * @param pos Position of the identifier to return. + * @return The requested identifier. + */ + [[nodiscard]] id_type operator[](const size_type pos) const { + return vertices.cbegin()[static_cast(pos)]; + } + + /*! @brief Clears the flow builder. */ + void clear() noexcept { + index.first() = {}; + vertices.clear(); + deps.clear(); + sync_on = {}; + } + + /** + * @brief Returns true if a flow builder contains no tasks, false otherwise. + * @return True if the flow builder contains no tasks, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return vertices.empty(); + } + + /** + * @brief Returns the number of tasks. + * @return The number of tasks. + */ + [[nodiscard]] size_type size() const noexcept { + return vertices.size(); + } + + /** + * @brief Binds a task to a flow builder. + * @param value Task identifier. + * @return This flow builder. + */ + basic_flow &bind(const id_type value) { + sync_on += (sync_on == vertices.size()); + const auto it = vertices.emplace(value).first; + index.first() = size_type(it - vertices.begin()); + return *this; + } + + /** + * @brief Turns the current task into a sync point. + * @return This flow builder. + */ + basic_flow &sync() { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + sync_on = index.first(); + + for(const auto &elem: deps) { + elem.second.emplace_back(sync_on, true); + } + + return *this; + } + + /** + * @brief Assigns a resource to the current task with a given access mode. + * @param res Resource identifier. + * @param is_rw Access mode. + * @return This flow builder. + */ + basic_flow &set(const id_type res, bool is_rw = false) { + emplace(res, is_rw); + return *this; + } + + /** + * @brief Assigns a read-only resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &ro(const id_type res) { + emplace(res, false); + return *this; + } + + /** + * @brief Assigns a range of read-only resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + ro(It first, It last) { + for(; first != last; ++first) { + emplace(*first, false); + } + + return *this; + } + + /** + * @brief Assigns a writable resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &rw(const id_type res) { + emplace(res, true); + return *this; + } + + /** + * @brief Assigns a range of writable resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + rw(It first, It last) { + for(; first != last; ++first) { + emplace(*first, true); + } + + return *this; + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency matrix of the task graph. + */ + [[nodiscard]] graph_type graph() const { + graph_type matrix{vertices.size(), get_allocator()}; + + setup_graph(matrix); + transitive_closure(matrix); + transitive_reduction(matrix); + + return matrix; + } + +private: + compressed_pair index; + task_container_type vertices; + deps_container_type deps; + size_type sync_on{}; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.hpp" + +// #include "component.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "storage.hpp" + +// #include "view.hpp" + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Registry Basic registry type. + */ +template +class as_view { + template + [[nodiscard]] auto dispatch(get_t, exclude_t) const { + return reg->template view...>(exclude_t...>{}); + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) noexcept + : reg{&source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Get Type of storage used to construct the view. + * @tparam Exclude Types of storage used to filter the view. + * @return A newly created view. + */ + template + operator basic_view() const { + return dispatch(Get{}, Exclude{}); + } + +private: + registry_type *reg; +}; + +/** + * @brief Converts a registry to a group. + * @tparam Registry Basic registry type. + */ +template +class as_group { + template + [[nodiscard]] auto dispatch(owned_t, get_t, exclude_t) const { + if constexpr(std::is_const_v) { + return reg->template group_if_exists(get_t{}, exclude_t{}); + } else { + return reg->template group...>(get_t...>{}, exclude_t...>{}); + } + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) noexcept + : reg{&source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Owned Types of _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + * @return A newly created group. + */ + template + operator basic_group() const { + return dispatch(Owned{}, Get{}, Exclude{}); + } + +private: + registry_type *reg; +}; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on an element of the given type. + * @tparam Registry Basic registry type. + * @param reg A registry that contains the given entity and its elements. + * @param entt Entity from which to get the element. + */ +template>> +void invoke(Registry ®, const typename Registry::entity_type entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + (reg.template get>(entt).*Member)(reg, entt); +} + +/** + * @brief Returns the entity associated with a given element. + * + * @warning + * Currently, this function only works correctly with the default storage as it + * makes assumptions about how the elements are laid out. + * + * @tparam Args Storage type template parameters. + * @param storage A storage that contains the given element. + * @param instance A valid element instance. + * @return The entity associated with the given element. + */ +template +typename basic_storage::entity_type to_entity(const basic_storage &storage, const typename basic_storage::value_type &instance) { + using traits_type = component_traits::value_type, typename basic_storage::entity_type>; + static_assert(traits_type::page_size != 0u, "Unexpected page size"); + const auto *page = storage.raw(); + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(std::size_t pos{}, count = storage.size(); pos < count; pos += traits_type::page_size, ++page) { + if(const auto dist = (std::addressof(instance) - *page); dist >= 0 && dist < static_cast(traits_type::page_size)) { + return *(static_cast::base_type &>(storage).rbegin() + static_cast(pos) + dist); + } + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + return null; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct sigh_helper; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + */ +template +struct sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + */ + sigh_helper(registry_type &ref) + : bucket{&ref} {} + + /** + * @brief Binds a properly initialized helper to a given signal type. + * @tparam Type Type of signal to bind the helper to. + * @param id Optional name for the underlying storage to use. + * @return A helper for a given registry and signal type. + */ + template + auto with(const id_type id = type_hash::value()) noexcept { + return sigh_helper{*bucket, id}; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type ®istry() noexcept { + return *bucket; + } + +private: + registry_type *bucket; +}; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + * @tparam Type Type of signal to connect listeners to. + */ +template +struct sigh_helper final: sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + * @param id Optional name for the underlying storage to use. + */ + sigh_helper(registry_type &ref, const id_type id = type_hash::value()) + : sigh_helper{ref}, + name{id} {} + + /** + * @brief Forwards the call to `on_construct` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_construct(Args &&...args) { + this->registry().template on_construct(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_update` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_update(Args &&...args) { + this->registry().template on_update(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_destroy` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_destroy(Args &&...args) { + this->registry().template on_destroy(name).template connect(std::forward(args)...); + return *this; + } + +private: + id_type name; +}; + +/** + * @brief Deduction guide. + * @tparam Registry Basic registry type. + */ +template +sigh_helper(Registry &) -> sigh_helper; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct is_view: std::false_type {}; + +template +struct is_view>: std::true_type {}; + +template +inline constexpr bool is_view_v = is_view::value; + +template +struct is_group: std::false_type {}; + +template +struct is_group>: std::true_type {}; + +template +inline constexpr bool is_group_v = is_group::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, exclude_t>, type_list> { + using ro = type_list_cat_t, typename unpack_type, type_list>::ro...>; + using rw = type_list_cat_t, type_list>::rw...>; +}; + +template +struct unpack_type, exclude_t>, type_list> + : unpack_type, exclude_t>, type_list> {}; + +template +struct unpack_type, get_t, exclude_t>, type_list> { + using ro = type_list_cat_t, typename unpack_type, type_list>::ro..., typename unpack_type, type_list>::ro...>; + using rw = type_list_cat_t, type_list>::rw..., typename unpack_type, type_list>::rw...>; +}; + +template +struct unpack_type, get_t, exclude_t>, type_list> + : unpack_type, get_t, exclude_t>, type_list> {}; + +template +struct resource_traits; + +template +struct resource_traits, 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...>; + static constexpr auto sync_point = (std::is_same_v || ...); +}; + +template +resource_traits...>, type_list> free_function_to_resource_traits(Ret (*)(Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (*)(Type &, Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const); + +} // namespace internal +/*! @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 Registry Basic registry type. + */ +template +class basic_organizer final { + using callback_type = void(const void *, Registry &); + using prepare_type = void(Registry &); + using dependency_type = std::size_t(const bool, const type_info **, const std::size_t); + + struct vertex_data final { + std::size_t ro_count{}; + std::size_t rw_count{}; + const char *name{}; + const void *payload{}; + callback_type *callback{}; + dependency_type *dependency{}; + prepare_type *prepare{}; + const type_info *info{}; + }; + + template + [[nodiscard]] static decltype(auto) extract(Registry ®) { + if constexpr(std::is_same_v) { + return reg; + } else if constexpr(internal::is_view_v) { + return static_cast(as_view{reg}); + } else if constexpr(internal::is_group_v) { + return static_cast(as_group{reg}); + } else { + return reg.ctx().template emplace>(); + } + } + + template + [[nodiscard]] static auto to_args(Registry ®, type_list) { + return std::tuple(reg))...>(extract(reg)...); + } + + template + [[nodiscard]] static std::size_t fill_dependencies(type_list, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) { + if constexpr(sizeof...(Type) == 0u) { + return {}; + } else { + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + const type_info *info[]{&type_id()...}; + const auto length = count < sizeof...(Type) ? count : sizeof...(Type); + + for(std::size_t pos{}; pos < length; ++pos) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + buffer[pos] = info[pos]; + } + + return length; + } + } + + template + void track_dependencies(std::size_t index, const bool sync_point, type_list, type_list) { + builder.bind(static_cast(index)); + builder.set(type_hash::value(), sync_point || (sizeof...(RO) + sizeof...(RW) == 0u)); + (builder.ro(type_hash::value()), ...); + (builder.rw(type_hash::value()), ...); + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @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 data The data associated with the vertex. + * @param from List of in-edges of the vertex. + * @param to List of out-edges of the vertex. + */ + vertex(vertex_data data, std::vector from, std::vector to) + : node{std::move(data)}, + in{std::move(from)}, + out{std::move(to)} {} + + /** + * @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. + */ + [[nodiscard]] size_type ro_dependency(const type_info **buffer, const std::size_t length) const 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. + */ + [[nodiscard]] size_type rw_dependency(const type_info **buffer, const std::size_t length) const 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. + */ + [[nodiscard]] size_type ro_count() const noexcept { + return node.ro_count; + } + + /** + * @brief Returns the number of writable resources of a vertex. + * @return The number of writable resources of the vertex. + */ + [[nodiscard]] size_type rw_count() const 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. + */ + [[nodiscard]] bool top_level() const noexcept { + return in.empty(); + } + + /** + * @brief Returns a type info object associated with a vertex. + * @return A properly initialized type info object. + */ + [[nodiscard]] const type_info &info() const 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. + */ + [[nodiscard]] const char *name() const noexcept { + return node.name; + } + + /** + * @brief Returns the function associated with a vertex. + * @return The function associated with the vertex. + */ + [[nodiscard]] function_type *callback() const noexcept { + return node.callback; + } + + /** + * @brief Returns the payload associated with a vertex, if any. + * @return The payload associated with the vertex, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return node.payload; + } + + /** + * @brief Returns the list of in-edges of a vertex. + * @return The list of in-edges of a vertex. + */ + [[nodiscard]] const std::vector &in_edges() const noexcept { + return in; + } + + /** + * @brief Returns the list of out-edges of a vertex. + * @return The list of out-edges of a vertex. + */ + [[nodiscard]] const std::vector &out_edges() const noexcept { + return out; + } + + /** + * @brief Prepares a registry and assures that all required resources + * are properly instantiated before using them. + * @param reg A valid registry. + */ + void prepare(registry_type ®) const { + node.prepare ? node.prepare(reg) : void(); + } + + private: + vertex_data node; + std::vector in; + std::vector out; + }; + + /** + * @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_traits(Candidate)); + + callback_type *callback = +[](const void *, registry_type ®) { + std::apply(Candidate, to_args(reg, typename resource_type::args{})); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + nullptr, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id>()}; + + track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds a free function with payload or a member function with an + * instance to the task list. + * @tparam Candidate Function or member to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @param name Optional name to associate with the task. + */ + template + void emplace(Type &value_or_instance, const char *name = nullptr) { + using resource_type = decltype(internal::constrained_function_to_resource_traits(Candidate)); + + callback_type *callback = +[](const void *payload, registry_type ®) { + 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{}))); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + &value_or_instance, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id>()}; + + track_dependencies(vertices.size(), resource_type::sync_point, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds an user defined function with optional payload to the task + * list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param func Function to add to the task list. + * @param payload User defined arbitrary data. + * @param name Optional name to associate with the task. + */ + template + void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) { + using resource_type = internal::resource_traits, type_list>; + track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + payload, + func, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + nullptr, + &type_id()}; + + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency list of the task graph. + */ + [[nodiscard]] std::vector graph() const { + std::vector adjacency_list{}; + adjacency_list.reserve(vertices.size()); + auto adjacency_matrix = builder.graph(); + + for(auto curr: adjacency_matrix.vertices()) { + std::vector in{}; + std::vector out{}; + + for(auto &&edge: adjacency_matrix.in_edges(curr)) { + in.push_back(edge.first); + } + + for(auto &&edge: adjacency_matrix.out_edges(curr)) { + out.push_back(edge.second); + } + + adjacency_list.emplace_back(vertices[curr], std::move(in), std::move(out)); + } + + return adjacency_list; + } + + /*! @brief Erases all elements from a container. */ + void clear() { + builder.clear(); + vertices.clear(); + } + +private: + std::vector vertices; + flow builder; +}; + +} // namespace entt + +#endif + +// #include "entity/ranges.hpp" +#ifndef ENTT_ENTITY_RANGES_HPP +#define ENTT_ENTITY_RANGES_HPP + +#if __has_include() +# include +# +# if defined(__cpp_lib_ranges) +# include +// # include "fwd.hpp" + + +template +inline constexpr bool std::ranges::enable_borrowed_range>{true}; + +template +inline constexpr bool std::ranges::enable_borrowed_range>{true}; + +template +inline constexpr bool std::ranges::enable_view>{true}; + +template +inline constexpr bool std::ranges::enable_view>{true}; + +# endif +#endif + +#endif +// #include "entity/registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator> +class dense_set; + +template +class basic_table; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Element types. + */ +template +using table = basic_table...>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits::max)(); + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + const dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + const dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept = default; + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return (offset = it[static_cast(offset)].next), *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + const dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + const auto idx = static_cast(offset); + return {it[idx].element.first, it[idx].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_map_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_map() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses a given element with bounds checking. + * @tparam Other Type of the key of an element to find. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../core/algorithm.hpp" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "mixin.hpp" +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP + +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../core/type_info.hpp" + +// #include "../signal/sigh.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_on_construct final: std::false_type {}; + +template +struct has_on_construct(), std::declval().create()))>> + : std::true_type {}; + +template +struct has_on_update final: std::false_type {}; + +template +struct has_on_update(), std::declval().create()))>> + : std::true_type {}; + +template +struct has_on_destroy final: std::false_type {}; + +template +struct has_on_destroy(), std::declval().create()))>> + : std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Mixin type used to add signal support to storage types. + * + * The function type of a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, entity_type); + * @endcode + * + * This applies to all signals made available. + * + * @tparam Type Underlying storage type. + * @tparam Registry Basic registry type. + */ +template +class basic_sigh_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using basic_registry_type = basic_registry; + using sigh_type = sigh; + using underlying_iterator = typename underlying_type::base_type::basic_iterator; + + static_assert(std::is_base_of_v, "Invalid registry type"); + + [[nodiscard]] auto &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + +private: + void pop(underlying_iterator first, underlying_iterator last) final { + if(auto ® = owner_or_assert(); destruction.empty()) { + underlying_type::pop(first, last); + } else { + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(reg, entt); + const auto it = underlying_type::find(entt); + underlying_type::pop(it, it + 1u); + } + } + } + + void pop_all() final { + if(auto ® = owner_or_assert(); !destruction.empty()) { + if constexpr(std::is_same_v) { + for(typename underlying_type::size_type pos{}, last = underlying_type::free_list(); pos < last; ++pos) { + destruction.publish(reg, underlying_type::base_type::operator[](pos)); + } + } else { + for(auto entt: static_cast(*this)) { + if constexpr(underlying_type::storage_policy == deletion_policy::in_place) { + if(entt != tombstone) { + destruction.publish(reg, entt); + } + } else { + destruction.publish(reg, entt); + } + } + } + } + + underlying_type::pop_all(); + } + + underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { + const auto it = underlying_type::try_emplace(entt, force_back, value); + + if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { + construction.publish(reg, *it); + } + + return it; + } + + void bind_any(any value) noexcept final { + owner = any_cast(&value); + + if constexpr(!std::is_same_v) { + if(owner == nullptr) { + owner = any_cast(&value); + } + } + + underlying_type::bind_any(std::move(value)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_sigh_mixin() + : basic_sigh_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sigh_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} { + if constexpr(internal::has_on_construct::value) { + sink{construction}.template connect<&underlying_type::element_type::on_construct>(); + } + + if constexpr(internal::has_on_update::value) { + sink{update}.template connect<&underlying_type::element_type::on_update>(); + } + + if constexpr(internal::has_on_destroy::value) { + sink{destruction}.template connect<&underlying_type::element_type::on_destroy>(); + } + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_sigh_mixin(const basic_sigh_mixin &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sigh_mixin(basic_sigh_mixin &&other) noexcept + : underlying_type{static_cast(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) + : underlying_type{static_cast(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} + + /*! @brief Default destructor. */ + ~basic_sigh_mixin() override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This mixin. + */ + basic_sigh_mixin &operator=(const basic_sigh_mixin &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This mixin. + */ + basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept { + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_sigh_mixin &other) noexcept { + using std::swap; + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); + underlying_type::swap(other); + } + + /** + * @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.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() noexcept { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; + } + + /** + * @brief Checks if a mixin refers to a valid registry. + * @return True if the mixin refers to a valid registry, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (owner != nullptr); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] const registry_type ®istry() const noexcept { + return owner_or_assert(); + } + + /*! @copydoc registry */ + [[nodiscard]] registry_type ®istry() noexcept { + return owner_or_assert(); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + auto generate() { + const auto entt = underlying_type::generate(); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type generate(const entity_type hint) { + const auto entt = underlying_type::generate(hint); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template + void generate(It first, It last) { + underlying_type::generate(first, last); + + if(auto ® = owner_or_assert(); !construction.empty()) { + for(; first != last; ++first) { + construction.publish(reg, *first); + } + } + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * @tparam Args Types of arguments to forward to the underlying storage. + * @param entt A valid identifier. + * @param args Parameters to forward to the underlying storage. + * @return A reference to the newly created object. + */ + template + decltype(auto) emplace(const entity_type entt, Args &&...args) { + underlying_type::emplace(entt, std::forward(args)...); + construction.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched instance. + */ + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + underlying_type::patch(entt, std::forward(func)...); + update.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * @tparam It Type of input iterator. + * @tparam Args Types of arguments to forward to the underlying storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param args Parameters to use to forward to the underlying storage. + */ + template + void insert(It first, It last, Args &&...args) { + auto from = underlying_type::size(); + underlying_type::insert(first, last, std::forward(args)...); + + if(auto ® = owner_or_assert(); !construction.empty()) { + // fine as long as insert passes force_back true to try_emplace + for(const auto to = underlying_type::size(); from != to; ++from) { + construction.publish(reg, underlying_type::operator[](from)); + } + } + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; + +/** + * @brief Mixin type used to add _reactive_ support to storage types. + * @tparam Type Underlying storage type. + * @tparam Registry Basic registry type. + */ +template +class basic_reactive_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using alloc_traits = std::allocator_traits; + using basic_registry_type = basic_registry; + using container_type = std::vector>; + + static_assert(std::is_base_of_v, "Invalid registry type"); + + [[nodiscard]] auto &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + + void emplace_element(const Registry &, typename underlying_type::entity_type entity) { + if(!underlying_type::contains(entity)) { + underlying_type::emplace(entity); + } + } + +private: + void bind_any(any value) noexcept final { + owner = any_cast(&value); + + if constexpr(!std::is_same_v) { + if(owner == nullptr) { + owner = any_cast(&value); + } + } + + underlying_type::bind_any(std::move(value)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_reactive_mixin() + : basic_reactive_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_reactive_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + conn{allocator} { + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_reactive_mixin(const basic_reactive_mixin &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_reactive_mixin(basic_reactive_mixin &&other) noexcept + : underlying_type{static_cast(other)}, + owner{other.owner}, + conn{std::move(other.conn)} { + } + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_reactive_mixin(basic_reactive_mixin &&other, const allocator_type &allocator) + : underlying_type{static_cast(other), allocator}, + owner{other.owner}, + conn{std::move(other.conn), allocator} { + } + + /*! @brief Default destructor. */ + ~basic_reactive_mixin() override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This mixin. + */ + basic_reactive_mixin &operator=(const basic_reactive_mixin &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This mixin. + */ + basic_reactive_mixin &operator=(basic_reactive_mixin &&other) noexcept { + underlying_type::swap(other); + return *this; + } + + /** + * @brief Makes storage _react_ to creation of objects of the given type. + * @tparam Clazz Type of element to _react_ to. + * @tparam Candidate Function to use to _react_ to the event. + * @param id Optional name used to map the storage within the registry. + * @return This mixin. + */ + template + basic_reactive_mixin &on_construct(const id_type id = type_hash::value()) { + auto curr = owner_or_assert().template storage(id).on_construct().template connect(*this); + conn.push_back(std::move(curr)); + return *this; + } + + /** + * @brief Makes storage _react_ to update of objects of the given type. + * @tparam Clazz Type of element to _react_ to. + * @tparam Candidate Function to use to _react_ to the event. + * @param id Optional name used to map the storage within the registry. + * @return This mixin. + */ + template + basic_reactive_mixin &on_update(const id_type id = type_hash::value()) { + auto curr = owner_or_assert().template storage(id).on_update().template connect(*this); + conn.push_back(std::move(curr)); + return *this; + } + + /** + * @brief Makes storage _react_ to destruction of objects of the given type. + * @tparam Clazz Type of element to _react_ to. + * @tparam Candidate Function to use to _react_ to the event. + * @param id Optional name used to map the storage within the registry. + * @return This mixin. + */ + template + basic_reactive_mixin &on_destroy(const id_type id = type_hash::value()) { + auto curr = owner_or_assert().template storage(id).on_destroy().template connect(*this); + conn.push_back(std::move(curr)); + return *this; + } + + /** + * @brief Checks if a mixin refers to a valid registry. + * @return True if the mixin refers to a valid registry, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (owner != nullptr); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] const registry_type ®istry() const noexcept { + return owner_or_assert(); + } + + /*! @copydoc registry */ + [[nodiscard]] registry_type ®istry() noexcept { + return owner_or_assert(); + } + + /** + * @brief Returns a view that is filtered by the underlying storage. + * @tparam Get Types of elements used to construct the view. + * @tparam Exclude Types of elements used to filter the view. + * @return A newly created view. + */ + template + [[nodiscard]] basic_view...>, exclude_t...>> + view(exclude_t = exclude_t{}) const { + const owner_type &parent = owner_or_assert(); + basic_view...>, exclude_t...>> elem{}; + [&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage>()..., parent.template storage>()..., this); + return elem; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view...>, exclude_t...>> + view(exclude_t = exclude_t{}) { + std::conditional_t<((std::is_const_v && ...) && (std::is_const_v && ...)), const owner_type, owner_type> &parent = owner_or_assert(); + return {*this, parent.template storage>()..., parent.template storage>()...}; + } + + /*! @brief Releases all connections to the underlying registry, if any. */ + void reset() { + for(auto &&curr: conn) { + curr.release(); + } + + conn.clear(); + } + +private: + basic_registry_type *owner; + container_type conn; +}; + +} // namespace entt + +#endif + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "view.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class registry_storage_iterator final { + template + friend class registry_storage_iterator; + + using mapped_type = std::remove_reference_t()->second)>; + +public: + using value_type = std::pair &>; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr registry_storage_iterator() noexcept + : it{} {} + + constexpr registry_storage_iterator(It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr registry_storage_iterator(const registry_storage_iterator &other) noexcept + : registry_storage_iterator{other.it} {} + + constexpr registry_storage_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr registry_storage_iterator operator++(int) noexcept { + const registry_storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr registry_storage_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr registry_storage_iterator operator--(int) noexcept { + const registry_storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr registry_storage_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr registry_storage_iterator operator+(const difference_type value) const noexcept { + registry_storage_iterator copy = *this; + return (copy += value); + } + + constexpr registry_storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr registry_storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, *it[value].second}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr std::ptrdiff_t operator-(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + + template + friend constexpr bool operator==(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + + template + friend constexpr bool operator<(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class registry_context { + using alloc_traits = std::allocator_traits; + using allocator_type = typename alloc_traits::template rebind_alloc>>; + +public: + explicit registry_context(const allocator_type &allocator) + : ctx{allocator} {} + + void clear() noexcept { + ctx.clear(); + } + + template + Type &emplace_as(const id_type id, Args &&...args) { + return any_cast(ctx.try_emplace(id, std::in_place_type, std::forward(args)...).first->second); + } + + template + Type &emplace(Args &&...args) { + return emplace_as(type_id().hash(), std::forward(args)...); + } + + template + Type &insert_or_assign(const id_type id, Type &&value) { + return any_cast> &>(ctx.insert_or_assign(id, std::forward(value)).first->second); + } + + template + Type &insert_or_assign(Type &&value) { + return insert_or_assign(type_id().hash(), std::forward(value)); + } + + template + bool erase(const id_type id = type_id().hash()) { + const auto it = ctx.find(id); + return it != ctx.end() && it->second.info() == type_id() ? (ctx.erase(it), true) : false; + } + + template + [[nodiscard]] const Type &get(const id_type id = type_id().hash()) const { + return any_cast(ctx.at(id)); + } + + template + [[nodiscard]] Type &get(const id_type id = type_id().hash()) { + return any_cast(ctx.at(id)); + } + + template + [[nodiscard]] const Type *find(const id_type id = type_id().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] Type *find(const id_type id = type_id().hash()) { + const auto it = ctx.find(id); + return it != ctx.end() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] bool contains(const id_type id = type_id().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() && it->second.info() == type_id(); + } + +private: + dense_map, identity, std::equal_to<>, allocator_type> ctx; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Fast and reliable entity-component system. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_registry { + using base_type = basic_sparse_set; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + // std::shared_ptr because of its type erased allocator which is useful here + using pool_container_type = dense_map, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc>>>; + using group_container_type = dense_map, identity, std::equal_to<>, typename alloc_traits::template rebind_alloc>>>; + using traits_type = entt_traits; + + template + [[nodiscard]] auto &assure([[maybe_unused]] const id_type id = type_hash::value()) { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + + if constexpr(std::is_same_v) { + ENTT_ASSERT(id == type_hash::value(), "User entity storage not allowed"); + return entities; + } else { + using storage_type = storage_for_type; + + if(auto it = pools.find(id); it != pools.cend()) { + ENTT_ASSERT(it->second->info() == type_id(), "Unexpected type"); + return static_cast(*it->second); + } + + using alloc_type = typename storage_type::allocator_type; + typename pool_container_type::mapped_type cpool{}; + + if constexpr(std::is_void_v && !std::is_constructible_v) { + // std::allocator has no cross constructors (waiting for C++20) + cpool = std::allocate_shared(get_allocator(), alloc_type{}); + } else { + cpool = std::allocate_shared(get_allocator(), get_allocator()); + } + + pools.emplace(id, cpool); + cpool->bind(*this); + + return static_cast(*cpool); + } + } + + template + [[nodiscard]] const auto *assure([[maybe_unused]] const id_type id = type_hash::value()) const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + + if constexpr(std::is_same_v) { + ENTT_ASSERT(id == type_hash::value(), "User entity storage not allowed"); + return &entities; + } else { + if(const auto it = pools.find(id); it != pools.cend()) { + ENTT_ASSERT(it->second->info() == type_id(), "Unexpected type"); + return static_cast *>(it->second.get()); + } + + return static_cast *>(nullptr); + } + } + + void rebind() { + entities.bind(*this); + + for(auto &&curr: pools) { + curr.second->bind(*this); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Context type. */ + using context = internal::registry_context; + /*! @brief Iterable registry type. */ + using iterable = iterable_adaptor>; + /*! @brief Constant iterable registry type. */ + using const_iterable = iterable_adaptor>; + + /** + * @copybrief storage_for + * @tparam Type Storage value type, eventually const. + */ + template + using storage_for_type = typename storage_for>>::type; + + /*! @brief Default constructor. */ + basic_registry() + : basic_registry{allocator_type{}} {} + + /** + * @brief Constructs an empty registry with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_registry(const allocator_type &allocator) + : basic_registry{0u, allocator} {} + + /** + * @brief Allocates enough memory upon construction to store `count` pools. + * @param count The number of pools to allocate memory for. + * @param allocator The allocator to use. + */ + basic_registry(const size_type count, const allocator_type &allocator = allocator_type{}) + : vars{allocator}, + pools{allocator}, + groups{allocator}, + entities{allocator} { + pools.reserve(count); + rebind(); + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_registry(const basic_registry &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_registry(basic_registry &&other) noexcept + : vars{std::move(other.vars)}, + pools{std::move(other.pools)}, + groups{std::move(other.groups)}, + entities{std::move(other.entities)} { + rebind(); + } + + /*! @brief Default destructor. */ + ~basic_registry() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This mixin. + */ + basic_registry &operator=(const basic_registry &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This registry. + */ + basic_registry &operator=(basic_registry &&other) noexcept { + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given registry. + * @param other Registry to exchange the content with. + */ + void swap(basic_registry &other) noexcept { + using std::swap; + + swap(vars, other.vars); + swap(pools, other.pools); + swap(groups, other.groups); + swap(entities, other.entities); + + rebind(); + other.rebind(); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return entities.get_allocator(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a registry. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage. + * + * @return An iterable object to use to _visit_ the registry. + */ + [[nodiscard]] iterable storage() noexcept { + return iterable{pools.begin(), pools.end()}; + } + + /*! @copydoc storage */ + [[nodiscard]] const_iterable storage() const noexcept { + return const_iterable{pools.cbegin(), pools.cend()}; + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return A pointer to the storage if it exists, a null pointer otherwise. + */ + [[nodiscard]] common_type *storage(const id_type id) { + return const_cast(std::as_const(*this).storage(id)); + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return A pointer to the storage if it exists, a null pointer otherwise. + */ + [[nodiscard]] const common_type *storage(const id_type id) const { + const auto it = pools.find(id); + return it == pools.cend() ? nullptr : it->second.get(); + } + + /** + * @brief Returns the storage for a given element type. + * @tparam Type Type of element of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given element type. + */ + template + storage_for_type &storage(const id_type id = type_hash::value()) { + return assure>(id); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given element type. + */ + template + [[nodiscard]] const storage_for_type *storage(const id_type id = type_hash::value()) const { + return assure>(id); + } + + /** + * @brief Discards the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return True in case of success, false otherwise. + */ + bool reset(const id_type id) { + ENTT_ASSERT(id != type_hash::value(), "Cannot reset entity storage"); + return !(pools.erase(id) == 0u); + } + + /** + * @brief Checks if an identifier refers to a valid entity. + * @param entt An identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ + [[nodiscard]] bool valid(const entity_type entt) const { + return static_cast(entities.find(entt).index()) < entities.free_list(); + } + + /** + * @brief Returns the actual version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if valid, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const { + return entities.current(entt); + } + + /** + * @brief Creates a new entity or recycles a destroyed one. + * @return A valid identifier. + */ + [[nodiscard]] entity_type create() { + return entities.generate(); + } + + /** + * @copybrief create + * + * If the requested entity isn't in use, the suggested identifier is used. + * Otherwise, a new identifier is generated. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + [[nodiscard]] entity_type create(const entity_type hint) { + return entities.generate(hint); + } + + /** + * @brief Assigns each element in a range an identifier. + * + * @sa create + * + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template + void create(It first, It last) { + entities.generate(std::move(first), std::move(last)); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * @warning + * Adding or removing elements to an entity that is being destroyed can + * result in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the recycled entity. + */ + version_type destroy(const entity_type entt) { + for(size_type pos = pools.size(); pos != 0u; --pos) { + pools.begin()[static_cast(pos - 1u)].second->remove(entt); + } + + entities.erase(entt); + return entities.current(entt); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa destroy + * + * @param entt A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ + version_type destroy(const entity_type entt, const version_type version) { + destroy(entt); + const auto elem = traits_type::construct(traits_type::to_entity(entt), version); + return entities.bump((elem == tombstone) ? traits_type::next(elem) : elem); + } + + /** + * @brief Destroys all entities in a range and releases their identifiers. + * + * @sa destroy + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void destroy(It first, It last) { + const auto to = entities.sort_as(first, last); + const auto from = entities.cend() - static_cast(entities.free_list()); + + for(auto &&curr: pools) { + curr.second->remove(from, to); + } + + entities.erase(from, to); + } + + /** + * @brief Assigns the given element to an entity. + * + * The element must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to assign an element to an entity that already owns it results + * in undefined behavior. + * + * @tparam Type Type of element to create. + * @tparam Args Types of arguments to use to construct the element. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the element. + * @return A reference to the newly created element. + */ + template + decltype(auto) emplace(const entity_type entt, Args &&...args) { + ENTT_ASSERT(valid(entt), "Invalid entity"); + return assure().emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns each entity in a range the given element. + * + * @sa emplace + * + * @tparam Type Type of element to create. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void insert(It first, It last) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity"); + assure().insert(std::move(first), std::move(last)); + } + + /** + * @brief Assigns each entity in a range the given element. + * + * @sa emplace + * + * @tparam Type Type of element to create. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the element to assign. + */ + template + void insert(It first, It last, const Type &value) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity"); + assure().insert(std::move(first), std::move(last), value); + } + + /** + * @brief Assigns each entity in a range the given elements. + * + * @sa emplace + * + * @tparam Type Type of element 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 elements. + */ + template::value_type, Type>>> + void insert(EIt first, EIt last, CIt from) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity"); + assure().insert(first, last, from); + } + + /** + * @brief Assigns or replaces the given element for an entity. + * + * @sa emplace + * @sa replace + * + * @tparam Type Type of element to assign or replace. + * @tparam Args Types of arguments to use to construct the element. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the element. + * @return A reference to the newly created element. + */ + template + decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args) { + auto &cpool = assure(); + ENTT_ASSERT(valid(entt), "Invalid entity"); + return cpool.contains(entt) ? cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }) : cpool.emplace(entt, std::forward(args)...); + } + + /** + * @brief Patches the given element for an entity. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * @warning + * Attempting to patch an element of an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Type of element to patch. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched element. + */ + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + return assure().patch(entt, std::forward(func)...); + } + + /** + * @brief Replaces the given element for an entity. + * + * The element must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to replace an element of an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Type of element to replace. + * @tparam Args Types of arguments to use to construct the element. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the element. + * @return A reference to the element being replaced. + */ + template + decltype(auto) replace(const entity_type entt, Args &&...args) { + return patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }); + } + + /** + * @brief Removes the given elements from an entity. + * @tparam Type Type of element to remove. + * @tparam Other Other types of elements to remove. + * @param entt A valid identifier. + * @return The number of elements actually removed. + */ + template + size_type remove(const entity_type entt) { + return (assure().remove(entt) + ... + assure().remove(entt)); + } + + /** + * @brief Removes the given elements from all the entities in a range. + * + * @sa remove + * + * @tparam Type Type of element to remove. + * @tparam Other Other types of elements to remove. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of elements actually removed. + */ + template + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v) { + std::array cpools{static_cast(&assure()), static_cast(&assure())...}; + + for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) { + if constexpr(sizeof...(Other) != 0u) { + if((*from)->data() == first.data()) { + std::swap((*from), cpools.back()); + } + } + + count += (*from)->remove(first, last); + } + + } else { + for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { + count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools); + } + } + + return count; + } + + /** + * @brief Erases the given elements from an entity. + * + * @warning + * Attempting to erase an element from an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Types of elements to erase. + * @tparam Other Other types of elements to erase. + * @param entt A valid identifier. + */ + template + void erase(const entity_type entt) { + (assure().erase(entt), (assure().erase(entt), ...)); + } + + /** + * @brief Erases the given elements from all the entities in a range. + * + * @sa erase + * + * @tparam Type Types of elements to erase. + * @tparam Other Other types of elements to erase. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void erase(It first, It last) { + if constexpr(std::is_same_v) { + std::array cpools{static_cast(&assure()), static_cast(&assure())...}; + + for(auto from = cpools.begin(), to = cpools.end(); from != to; ++from) { + if constexpr(sizeof...(Other) != 0u) { + if((*from)->data() == first.data()) { + std::swap(*from, cpools.back()); + } + } + + (*from)->erase(first, last); + } + } else { + for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { + std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); + } + } + } + + /** + * @brief Erases elements satisfying specific criteria from an entity. + * + * The function type is equivalent to: + * + * @code{.cpp} + * void(const id_type, typename basic_registry::common_type &); + * @endcode + * + * Only storage where the entity exists are passed to the function. + * + * @tparam Func Type of the function object to invoke. + * @param entt A valid identifier. + * @param func A valid function object. + */ + template + void erase_if(const entity_type entt, Func func) { + for(auto [id, cpool]: storage()) { + if(cpool.contains(entt) && func(id, std::as_const(cpool))) { + cpool.erase(entt); + } + } + } + + /** + * @brief Removes all tombstones from a registry or only the pools for the + * given elements. + * @tparam Type Types of elements for which to clear all tombstones. + */ + template + void compact() { + if constexpr(sizeof...(Type) == 0u) { + for(auto &&curr: pools) { + curr.second->compact(); + } + } else { + (assure().compact(), ...); + } + } + + /** + * @brief Check if an entity is part of all the given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of all the storage, false otherwise. + */ + template + [[nodiscard]] bool all_of([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + auto *cpool = assure...>(); + return cpool && cpool->contains(entt); + } else { + return (all_of(entt) && ...); + } + } + + /** + * @brief Check if an entity is part of at least one given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of at least one storage, false + * otherwise. + */ + template + [[nodiscard]] bool any_of([[maybe_unused]] const entity_type entt) const { + return (all_of(entt) || ...); + } + + /** + * @brief Returns references to the given elements for an entity. + * + * @warning + * Attempting to get an element from an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Types of elements to get. + * @param entt A valid identifier. + * @return References to the elements owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + return (assure>()->get(entt), ...); + } else { + return std::forward_as_tuple(get(entt)...); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) { + if constexpr(sizeof...(Type) == 1u) { + return (static_cast &>(assure>()).get(entt), ...); + } else { + return std::forward_as_tuple(get(entt)...); + } + } + + /** + * @brief Returns a reference to the given element for an entity. + * + * In case the entity doesn't own the element, the parameters provided are + * used to construct it. + * + * @sa get + * @sa emplace + * + * @tparam Type Type of element to get. + * @tparam Args Types of arguments to use to construct the element. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the element. + * @return Reference to the element owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) { + auto &cpool = assure(); + ENTT_ASSERT(valid(entt), "Invalid entity"); + return cpool.contains(entt) ? cpool.get(entt) : cpool.emplace(entt, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given elements for an entity. + * + * @note + * The registry retains ownership of the pointed-to elements. + * + * @tparam Type Types of elements to get. + * @param entt A valid identifier. + * @return Pointers to the elements owned by the entity. + */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + const auto *cpool = assure...>(); + return (cpool && cpool->contains(entt)) ? std::addressof(cpool->get(entt)) : nullptr; + } else { + return std::make_tuple(try_get(entt)...); + } + } + + /*! @copydoc try_get */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) { + if constexpr(sizeof...(Type) == 1u) { + return (const_cast(std::as_const(*this).template try_get(entt)), ...); + } else { + return std::make_tuple(try_get(entt)...); + } + } + + /** + * @brief Clears a whole registry or the pools for the given elements. + * @tparam Type Types of elements to remove from their entities. + */ + template + void clear() { + if constexpr(sizeof...(Type) == 0u) { + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[static_cast(pos - 1u)].second->clear(); + } + + const auto elem = entities.each(); + entities.erase(elem.begin().base(), elem.end().base()); + } else { + (assure().clear(), ...); + } + } + + /** + * @brief Checks if an entity has elements assigned. + * @param entt A valid identifier. + * @return True if the entity has no elements assigned, false otherwise. + */ + [[nodiscard]] bool orphan(const entity_type entt) const { + return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); }); + } + + /** + * @brief Returns a sink object for the given element. + * + * Use this function to receive notifications whenever a new instance of the + * given element 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** assigning the element to the entity. + * + * @sa sink + * + * @tparam Type Type of element of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_construct(const id_type id = type_hash::value()) { + return assure(id).on_construct(); + } + + /** + * @brief Returns a sink object for the given element. + * + * Use this function to receive notifications whenever an instance of the + * given element is explicitly updated.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **after** updating the element. + * + * @sa sink + * + * @tparam Type Type of element of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_update(const id_type id = type_hash::value()) { + return assure(id).on_update(); + } + + /** + * @brief Returns a sink object for the given element. + * + * Use this function to receive notifications whenever an instance of the + * given element 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** removing the element from the entity. + * + * @sa sink + * + * @tparam Type Type of element of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_destroy(const id_type id = type_hash::value()) { + return assure(id).on_destroy(); + } + + /** + * @brief Returns a view for the given elements. + * @tparam Type Type of element used to construct the view. + * @tparam Other Other types of elements used to construct the view. + * @tparam Exclude Types of elements used to filter the view. + * @return A newly created view. + */ + template + [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> + view(exclude_t = exclude_t{}) const { + basic_view, storage_for_type...>, exclude_t...>> elem{}; + [&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(assure>()..., assure>()..., assure>()); + return elem; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> + view(exclude_t = exclude_t{}) { + return {assure>(), assure>()..., assure>()...}; + } + + /** + * @brief Returns a group for the given elements. + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group, if any. + * @tparam Exclude Types of storage used to filter the group, if any. + * @return A newly created group. + */ + template + basic_group...>, get_t...>, exclude_t...>> + group(get_t = get_t{}, exclude_t = exclude_t{}) { + using group_type = basic_group...>, get_t...>, exclude_t...>>; + using handler_type = typename group_type::handler; + + if(auto it = groups.find(group_type::group_id()); it != groups.cend()) { + return {*std::static_pointer_cast(it->second)}; + } + + std::shared_ptr handler{}; + + if constexpr(sizeof...(Owned) == 0u) { + handler = std::allocate_shared(get_allocator(), get_allocator(), std::forward_as_tuple(assure>()...), std::forward_as_tuple(assure>()...)); + } else { + handler = std::allocate_shared(get_allocator(), std::forward_as_tuple(assure>()..., assure>()...), std::forward_as_tuple(assure>()...)); + ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [](const auto &data) { return !(data.second->owned(type_id().hash()) || ...); }), "Conflicting groups"); + } + + groups.emplace(group_type::group_id(), handler); + return {*handler}; + } + + /*! @copydoc group */ + template + [[nodiscard]] basic_group...>, get_t...>, exclude_t...>> + group_if_exists(get_t = get_t{}, exclude_t = exclude_t{}) const { + using group_type = basic_group...>, get_t...>, exclude_t...>>; + using handler_type = typename group_type::handler; + + if(auto it = groups.find(group_type::group_id()); it != groups.cend()) { + return {*std::static_pointer_cast(it->second)}; + } + + return {}; + } + + /** + * @brief Checks whether the given elements belong to any group. + * @tparam Type Types of elements in which one is interested. + * @return True if the pools of the given elements are _free_, false + * otherwise. + */ + template + [[nodiscard]] bool owned() const { + return std::any_of(groups.cbegin(), groups.cend(), [](auto &&data) { return (data.second->owned(type_id().hash()) || ...); }); + } + + /** + * @brief Sorts the elements of a given element. + * + * The comparison function object returns `true` if the first element is + * _less_ than the second one, `false` otherwise. Its signature is also + * equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Type &, const Type &); + * @endcode + * + * Moreover, it shall induce a _strict weak ordering_ on the values.
+ * The sort function object offers an `operator()` that accepts: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function object to use to compare the elements. + * + * The comparison function object hasn't necessarily the type of the one + * passed along with the other parameters to this member function. + * + * @warning + * Pools of elements owned by a group cannot be sorted. + * + * @tparam Type Type of elements 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(!owned(), "Cannot sort owned storage"); + auto &cpool = assure(); + + if constexpr(std::is_invocable_v) { + auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; + cpool.sort(std::move(comp), std::move(algo), std::forward(args)...); + } else { + cpool.sort(std::move(compare), std::move(algo), std::forward(args)...); + } + } + + /** + * @brief Sorts two pools of elements in the same way. + * + * Entities and elements in `To` which are part of both storage are sorted + * internally with the order they have in `From`. The others follow in no + * particular order. + * + * @warning + * Pools of elements owned by a group cannot be sorted. + * + * @tparam To Type of elements to sort. + * @tparam From Type of elements to use to sort. + */ + template + void sort() { + ENTT_ASSERT(!owned(), "Cannot sort owned storage"); + const base_type &cpool = assure(); + assure().sort_as(cpool.begin(), cpool.end()); + } + + /** + * @brief Returns the context object, that is, a general purpose container. + * @return The context object, that is, a general purpose container. + */ + [[nodiscard]] context &ctx() noexcept { + return vars; + } + + /*! @copydoc ctx */ + [[nodiscard]] const context &ctx() const noexcept { + return vars; + } + +private: + context vars; + pool_container_type pools; + group_container_type groups; + storage_for_type entities; +}; + +} // namespace entt + +#endif + +// #include "entity/runtime_view.hpp" +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + +#include +#include +#include +#include +#include +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class runtime_view_iterator final { + using iterator_type = typename Set::iterator; + using iterator_traits = std::iterator_traits; + + [[nodiscard]] bool valid() const { + return (!tombstone_check || *it != tombstone) + && std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); + } + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::bidirectional_iterator_tag; + + constexpr runtime_view_iterator() noexcept + : pools{}, + filter{}, + it{}, + tombstone_check{} {} + + runtime_view_iterator(const std::vector &cpools, iterator_type curr, const std::vector &ignore) noexcept + : pools{&cpools}, + filter{&ignore}, + it{curr}, + tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} { + if(it != (*pools)[0]->end() && !valid()) { + ++(*this); + } + } + + runtime_view_iterator &operator++() { + ++it; + for(const auto last = (*pools)[0]->end(); it != last && !valid(); ++it) {} + return *this; + } + + runtime_view_iterator operator++(int) { + const runtime_view_iterator orig = *this; + return ++(*this), orig; + } + + runtime_view_iterator &operator--() { + --it; + for(const auto first = (*pools)[0]->begin(); it != first && !valid(); --it) {} + return *this; + } + + runtime_view_iterator operator--(int) { + const runtime_view_iterator orig = *this; + return operator--(), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return it.operator->(); + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept { + return it == other.it; + } + + [[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept { + return !(*this == other); + } + +private: + const std::vector *pools; + const std::vector *filter; + iterator_type it; + bool tombstone_check; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Generic runtime view. + * + * Runtime views iterate over those entities that are at least in the given + * storage. During initialization, a runtime view looks at the number of + * entities available for each element and uses the smallest set in order to get + * a performance boost when iterating. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, elements are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the storage iterated by the view in any way + * invalidates all the iterators. + * + * @tparam Type Common base type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_runtime_view { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector; + + [[nodiscard]] auto offset() const noexcept { + ENTT_ASSERT(!pools.empty(), "Invalid view"); + const auto &leading = *pools.front(); + return (leading.policy() == deletion_policy::swap_only) ? leading.free_list() : leading.size(); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::runtime_view_iterator; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_runtime_view() noexcept + : basic_runtime_view{allocator_type{}} {} + + /** + * @brief Constructs an empty, invalid view with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_runtime_view(const allocator_type &allocator) + : pools{allocator}, + filter{allocator} {} + + /*! @brief Default copy constructor. */ + basic_runtime_view(const basic_runtime_view &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator) + : pools{other.pools, allocator}, + filter{other.filter, allocator} {} + + /*! @brief Default move constructor. */ + basic_runtime_view(basic_runtime_view &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator) + : pools{std::move(other.pools), allocator}, + filter{std::move(other.filter), allocator} {} + + /*! @brief Default destructor. */ + ~basic_runtime_view() = default; + + /** + * @brief Default copy assignment operator. + * @return This runtime view. + */ + basic_runtime_view &operator=(const basic_runtime_view &) = default; + + /** + * @brief Default move assignment operator. + * @return This runtime view. + */ + basic_runtime_view &operator=(basic_runtime_view &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given view. + * @param other View to exchange the content with. + */ + void swap(basic_runtime_view &other) noexcept { + using std::swap; + swap(pools, other.pools); + swap(filter, other.filter); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pools.get_allocator(); + } + + /*! @brief Clears the view. */ + void clear() { + pools.clear(); + filter.clear(); + } + + /** + * @brief Appends an opaque storage object to a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ + basic_runtime_view &iterate(common_type &base) { + if(pools.empty() || !(base.size() < pools.front()->size())) { + pools.push_back(&base); + } else { + pools.push_back(std::exchange(pools.front(), &base)); + } + + return *this; + } + + /** + * @brief Adds an opaque storage object as a filter of a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ + basic_runtime_view &exclude(common_type &base) { + filter.push_back(&base); + return *this; + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const { + return pools.empty() ? size_type{} : offset(); + } + + /** + * @brief Returns an iterator to the first entity that has the given + * elements. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity that has the given elements. + */ + [[nodiscard]] iterator begin() const { + return pools.empty() ? iterator{} : iterator{pools, pools.front()->end() - static_cast(offset()), filter}; + } + + /** + * @brief Returns an iterator that is past the last entity that has the + * given elements. + * @return An iterator to the entity following the last entity that has the + * given elements. + */ + [[nodiscard]] iterator end() const { + return pools.empty() ? iterator{} : iterator{pools, pools.front()->end(), filter}; + } + + /** + * @brief Checks whether a view is initialized or not. + * @return True if the view is initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(pools.empty() && filter.empty()); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const { + return !pools.empty() + && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }) + && pools.front()->index(entt) < offset(); + } + + /** + * @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.
+ * 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: + container_type pools; + container_type filter; +}; + +} // namespace entt + +#endif + +// #include "entity/snapshot.hpp" +#ifndef ENTT_ENTITY_SNAPSHOT_HPP +#define ENTT_ENTITY_SNAPSHOT_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "view.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +void orphans(Registry ®istry) { + auto &storage = registry.template storage(); + + for(auto entt: storage) { + if(registry.orphan(entt)) { + storage.erase(entt); + } + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Utility class to create snapshots from a registry. + * + * A _snapshot_ can be either a dump of the entire registry or a narrower + * selection of elements of interest.
+ * This type can be used in both cases if provided with a correctly configured + * output archive. + * + * @tparam Registry Basic registry type. + */ +template +class basic_snapshot { + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = entt_traits; + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot(const registry_type &source) noexcept + : reg{&source} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_snapshot(const basic_snapshot &) = delete; + + /*! @brief Default move constructor. */ + basic_snapshot(basic_snapshot &&) noexcept = default; + + /*! @brief Default destructor. */ + ~basic_snapshot() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This snapshot. + */ + basic_snapshot &operator=(const basic_snapshot &) = delete; + + /** + * @brief Default move assignment operator. + * @return This snapshot. + */ + basic_snapshot &operator=(basic_snapshot &&) noexcept = default; + + /** + * @brief Serializes all elements of a type with associated identifiers. + * @tparam Type Type of elements to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @param id Optional name used to map the storage within the registry. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot &get(Archive &archive, const id_type id = type_hash::value()) const { + if(const auto *storage = reg->template storage(id); storage) { + const typename registry_type::common_type &base = *storage; + + archive(static_cast(storage->size())); + + if constexpr(std::is_same_v) { + archive(static_cast(storage->free_list())); + + for(auto first = base.rbegin(), last = base.rend(); first != last; ++first) { + archive(*first); + } + } else if constexpr(registry_type::template storage_for_type::storage_policy == deletion_policy::in_place) { + for(auto it = base.rbegin(), last = base.rend(); it != last; ++it) { + if(const auto entt = *it; entt == tombstone) { + archive(static_cast(null)); + } else { + archive(entt); + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, storage->get_as_tuple(entt)); + } + } + } else { + for(auto elem: storage->reach()) { + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, elem); + } + } + } else { + archive(typename traits_type::entity_type{}); + } + + return *this; + } + + /** + * @brief Serializes all elements of a type with associated identifiers for + * the entities in a range. + * @tparam Type Type of elements to serialize. + * @tparam Archive Type of output archive. + * @tparam It Type of input iterator. + * @param archive A valid reference to an output archive. + * @param first An iterator to the first element of the range to serialize. + * @param last An iterator past the last element of the range to serialize. + * @param id Optional name used to map the storage within the registry. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash::value()) const { + static_assert(!std::is_same_v, "Entity types not supported"); + + if(const auto *storage = reg->template storage(id); storage && !storage->empty()) { + archive(static_cast(std::distance(first, last))); + + for(; first != last; ++first) { + if(const auto entt = *first; storage->contains(entt)) { + archive(entt); + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, storage->get_as_tuple(entt)); + } else { + archive(static_cast(null)); + } + } + } else { + archive(typename traits_type::entity_type{}); + } + + return *this; + } + +private: + const registry_type *reg; +}; + +/** + * @brief Utility class to restore a snapshot as a whole. + * + * A snapshot loader requires that the destination registry be empty and loads + * all the data at once while keeping intact the identifiers that the entities + * originally had.
+ * An example of use is the implementation of a save/restore utility. + * + * @tparam Registry Basic registry type. + */ +template +class basic_snapshot_loader { + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = entt_traits; + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot_loader(registry_type &source) noexcept + : reg{&source} { + // restoring a snapshot as a whole requires a clean registry + ENTT_ASSERT(reg->template storage().free_list() == 0u, "Registry must be empty"); + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_snapshot_loader(const basic_snapshot_loader &) = delete; + + /*! @brief Default move constructor. */ + basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default; + + /*! @brief Default destructor. */ + ~basic_snapshot_loader() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This loader. + */ + basic_snapshot_loader &operator=(const basic_snapshot_loader &) = delete; + + /** + * @brief Default move assignment operator. + * @return This loader. + */ + basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default; + + /** + * @brief Restores all elements of a type with associated identifiers. + * @tparam Type Type of elements to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @param id Optional name used to map the storage within the registry. + * @return A valid loader to continue restoring data. + */ + template + basic_snapshot_loader &get(Archive &archive, const id_type id = type_hash::value()) { + auto &storage = reg->template storage(id); + typename traits_type::entity_type length{}; + + archive(length); + + if constexpr(std::is_same_v) { + typename traits_type::entity_type count{}; + entity_type placeholder{}; + + storage.reserve(length); + archive(count); + + for(entity_type entity = null; length; --length) { + archive(entity); + storage.generate(entity); + placeholder = (entity > placeholder) ? entity : placeholder; + } + + storage.start_from(traits_type::next(placeholder)); + storage.free_list(count); + } else { + auto &other = reg->template storage(); + entity_type entt{null}; + + while(length--) { + if(archive(entt); entt != null) { + const auto entity = other.contains(entt) ? entt : other.generate(entt); + ENTT_ASSERT(entity == entt, "Entity not available for use"); + + if constexpr(std::tuple_size_v == 0u) { + storage.emplace(entity); + } else { + Type elem{}; + archive(elem); + storage.emplace(entity, std::move(elem)); + } + } + } + } + + return *this; + } + + /** + * @brief Destroys those entities that have no elements. + * + * In case all the entities were serialized but only part of the elements + * was saved, it could happen that some of the entities have no elements + * once restored.
+ * This function helps to identify and destroy those entities. + * + * @return A valid loader to continue restoring data. + */ + basic_snapshot_loader &orphans() { + internal::orphans(*reg); + return *this; + } + +private: + registry_type *reg; +}; + +/** + * @brief Utility class for _continuous loading_. + * + * A _continuous loader_ is designed to load data from a source registry to a + * (possibly) non-empty destination. The loader can accommodate in a registry + * more than one snapshot in a sort of _continuous loading_ that updates the + * destination one step at a time.
+ * Identifiers that entities originally had are not transferred to the target. + * Instead, the loader maps remote identifiers to local ones while restoring a + * snapshot.
+ * An example of use is the implementation of a client-server application with + * the requirement of transferring somehow parts of the representation side to + * side. + * + * @tparam Registry Basic registry type. + */ +template +class basic_continuous_loader { + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = entt_traits; + + void restore(typename Registry::entity_type entt) { + if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) { + if(!reg->valid(remloc[entity].second)) { + remloc[entity].second = reg->create(); + } + } else { + remloc.insert_or_assign(entity, std::make_pair(entt, reg->create())); + } + } + + template + auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) { + // map like container + Container other; + + for(auto &&pair: container) { + using first_type = std::remove_const_t::first_type>; + using second_type = typename std::decay_t::second_type; + + if constexpr(std::is_same_v && std::is_same_v) { + other.emplace(map(pair.first), map(pair.second)); + } else if constexpr(std::is_same_v) { + other.emplace(map(pair.first), std::move(pair.second)); + } else { + static_assert(std::is_same_v, "Neither the key nor the value are of entity type"); + other.emplace(std::move(pair.first), map(pair.second)); + } + } + + using std::swap; + swap(container, other); + } + + template + auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) { + // vector like container + static_assert(std::is_same_v, "Invalid value type"); + + for(auto &&entt: container) { + entt = map(entt); + } + } + + template + void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*member) { + if constexpr(!std::is_same_v) { + return; + } else if constexpr(std::is_same_v) { + instance.*member = map(instance.*member); + } else { + // maybe a container? let's try... + update(0, instance.*member); + } + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_continuous_loader(registry_type &source) noexcept + : remloc{source.get_allocator()}, + reg{&source} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_continuous_loader(const basic_continuous_loader &) = delete; + + /*! @brief Default move constructor. */ + basic_continuous_loader(basic_continuous_loader &&) noexcept = default; + + /*! @brief Default destructor. */ + ~basic_continuous_loader() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This loader. + */ + basic_continuous_loader &operator=(const basic_continuous_loader &) = delete; + + /** + * @brief Default move assignment operator. + * @return This loader. + */ + basic_continuous_loader &operator=(basic_continuous_loader &&) noexcept = default; + + /** + * @brief Restores all elements of a type with associated identifiers. + * + * It creates local counterparts for remote elements as needed.
+ * Members are either data members of type entity_type or containers of + * entities. In both cases, a loader visits them and replaces entities with + * their local counterpart. + * + * @tparam Type Type of elements to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @param id Optional name used to map the storage within the registry. + * @return A valid loader to continue restoring data. + */ + template + basic_continuous_loader &get(Archive &archive, const id_type id = type_hash::value()) { + auto &storage = reg->template storage(id); + typename traits_type::entity_type length{}; + entity_type entt{null}; + + archive(length); + + if constexpr(std::is_same_v) { + typename traits_type::entity_type in_use{}; + + storage.reserve(length); + archive(in_use); + + for(std::size_t pos{}; pos < in_use; ++pos) { + archive(entt); + restore(entt); + } + + for(std::size_t pos = in_use; pos < length; ++pos) { + archive(entt); + + if(const auto entity = to_entity(entt); remloc.contains(entity)) { + if(reg->valid(remloc[entity].second)) { + reg->destroy(remloc[entity].second); + } + + remloc.erase(entity); + } + } + } else { + for(auto &&ref: remloc) { + storage.remove(ref.second.second); + } + + while(length--) { + if(archive(entt); entt != null) { + restore(entt); + + if constexpr(std::tuple_size_v == 0u) { + storage.emplace(map(entt)); + } else { + Type elem{}; + archive(elem); + storage.emplace(map(entt), std::move(elem)); + } + } + } + } + + return *this; + } + + /** + * @brief Destroys those entities that have no elements. + * + * In case all the entities were serialized but only part of the elements + * was saved, it could happen that some of the entities have no elements + * once restored.
+ * This function helps to identify and destroy those entities. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader &orphans() { + internal::orphans(*reg); + return *this; + } + + /** + * @brief Tests if a loader knows about a given entity. + * @param entt A valid identifier. + * @return True if `entity` is managed by the loader, false otherwise. + */ + [[nodiscard]] bool contains(entity_type entt) const noexcept { + const auto it = remloc.find(to_entity(entt)); + return it != remloc.cend() && it->second.first == entt; + } + + /** + * @brief Returns the identifier to which an entity refers. + * @param entt A valid identifier. + * @return The local identifier if any, the null entity otherwise. + */ + [[nodiscard]] entity_type map(entity_type entt) const noexcept { + if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) { + return it->second.second; + } + + return null; + } + +private: + dense_map> remloc; + registry_type *reg; +}; + +} // namespace entt + +#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 "../core/any.hpp" + +// #include "../core/bit.hpp" + +// #include "../core/type_info.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct sparse_set_iterator final { + using value_type = typename Container::value_type; + using pointer = typename Container::const_pointer; + using reference = typename Container::const_reference; + using difference_type = typename Container::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr sparse_set_iterator() noexcept + : packed{}, + offset{} {} + + constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept + : packed{&ref}, + offset{idx} {} + + constexpr sparse_set_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr sparse_set_iterator operator++(int) noexcept { + const sparse_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr sparse_set_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr sparse_set_iterator operator--(int) noexcept { + const sparse_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { + sparse_set_iterator copy = *this; + return (copy += value); + } + + constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return (*packed)[static_cast(index() - value)]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr pointer data() const noexcept { + return packed ? packed->data() : nullptr; + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + const Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief 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 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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector; + using traits_type = entt_traits; + + static constexpr auto max_size = static_cast(traits_type::to_entity(null)); + + // it could be auto but gcc complains and emits a warning due to a false positive + [[nodiscard]] std::size_t policy_to_head() const noexcept { + return static_cast(max_size * static_cast>(mode != deletion_policy::swap_only)); + } + + [[nodiscard]] auto entity_to_pos(const Entity entt) const noexcept { + return static_cast(traits_type::to_entity(entt)); + } + + [[nodiscard]] auto pos_to_page(const std::size_t pos) const noexcept { + return static_cast(pos / traits_type::page_size); + } + + [[nodiscard]] auto sparse_ptr(const Entity entt) const { + const auto pos = entity_to_pos(entt); + const auto page = pos_to_page(pos); + return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; + } + + [[nodiscard]] auto &sparse_ref(const Entity entt) const { + ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); + const auto pos = entity_to_pos(entt); + return sparse[pos_to_page(pos)][fast_mod(pos, traits_type::page_size)]; + } + + [[nodiscard]] auto to_iterator(const Entity entt) const { + return --(end() - static_cast(index(entt))); + } + + [[nodiscard]] auto &assure_at_least(const Entity entt) { + const auto pos = entity_to_pos(entt); + const auto page = pos_to_page(pos); + + if(!(page < sparse.size())) { + sparse.resize(page + 1u, nullptr); + } + + if(!sparse[page]) { + constexpr entity_type init = null; + auto page_allocator{packed.get_allocator()}; + sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); + std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init); + } + + return sparse[page][fast_mod(pos, traits_type::page_size)]; + } + + void release_sparse_pages() { + auto page_allocator{packed.get_allocator()}; + + for(auto &&page: sparse) { + if(page != nullptr) { + std::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); + page = nullptr; + } + } + } + + void swap_at(const std::size_t lhs, const std::size_t rhs) { + auto &from = packed[lhs]; + auto &to = packed[rhs]; + + sparse_ref(from) = traits_type::combine(static_cast(rhs), traits_type::to_integral(from)); + sparse_ref(to) = traits_type::combine(static_cast(lhs), traits_type::to_integral(to)); + + std::swap(from, to); + } + +private: + [[nodiscard]] virtual const void *get_at(const std::size_t) const { + return nullptr; + } + + virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) { + ENTT_ASSERT((mode != deletion_policy::swap_only) || ((lhs < head) == (rhs < head)), "Cross swapping is not supported"); + } + +protected: + /*! @brief Random access iterator type. */ + using basic_iterator = internal::sparse_set_iterator; + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_only(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch"); + const auto pos = index(*it); + bump(traits_type::next(*it)); + swap_at(pos, head -= (pos < head)); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_and_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch"); + auto &self = sparse_ref(*it); + const auto entt = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); + packed[static_cast(entt)] = packed.back(); + // unnecessary but it helps to detect nasty bugs + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT((packed.back() = null, true), ""); + // lazy self-assignment guard + self = null; + packed.pop_back(); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void in_place_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch"); + const auto pos = entity_to_pos(std::exchange(sparse_ref(*it), null)); + packed[pos] = traits_type::combine(static_cast(std::exchange(head, pos)), tombstone); + } + + /** + * @brief Erases entities from a sparse set. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + virtual void pop(basic_iterator first, basic_iterator last) { + switch(mode) { + case deletion_policy::swap_and_pop: + for(; first != last; ++first) { + swap_and_pop(first); + } + break; + case deletion_policy::in_place: + for(; first != last; ++first) { + in_place_pop(first); + } + break; + case deletion_policy::swap_only: + for(; first != last; ++first) { + swap_only(first); + } + break; + } + } + + /*! @brief Erases all entities of a sparse set. */ + virtual void pop_all() { + switch(mode) { + case deletion_policy::in_place: + if(head != max_size) { + for(auto &&elem: packed) { + if(elem != tombstone) { + sparse_ref(elem) = null; + } + } + break; + } + [[fallthrough]]; + case deletion_policy::swap_only: + case deletion_policy::swap_and_pop: + for(auto &&elem: packed) { + sparse_ref(elem) = null; + } + break; + } + + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Assigns an entity to a sparse set. + * @param entt A valid identifier. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) { + ENTT_ASSERT(entt != null && entt != tombstone, "Invalid element"); + auto &elem = assure_at_least(entt); + auto pos = size(); + + switch(mode) { + case deletion_policy::in_place: + if(head != max_size && !force_back) { + pos = head; + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast(head), traits_type::to_integral(entt)); + head = entity_to_pos(std::exchange(packed[pos], entt)); + break; + } + [[fallthrough]]; + case deletion_policy::swap_and_pop: + packed.push_back(entt); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + break; + case deletion_policy::swap_only: + if(elem == null) { + packed.push_back(entt); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + } else { + ENTT_ASSERT(!(entity_to_pos(elem) < head), "Slot not available"); + bump(entt); + } + + pos = head++; + swap_at(entity_to_pos(elem), pos); + break; + } + + return iterator{packed, static_cast(++pos)}; + } + + /*! @brief Forwards variables to derived classes, if any. */ + // NOLINTNEXTLINE(performance-unnecessary-value-param) + virtual void bind_any(any) noexcept {} + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Pointer type to contained entities. */ + using pointer = typename packed_container_type::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = basic_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + + /*! @brief Default constructor. */ + basic_sparse_set() + : basic_sparse_set{type_id()} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sparse_set(const allocator_type &allocator) + : basic_sparse_set{deletion_policy::swap_and_pop, allocator} {} + + /** + * @brief Constructs an empty container with the given policy and allocator. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {}) + : basic_sparse_set{type_id(), pol, allocator} {} + + /** + * @brief Constructs an empty container with the given value type, policy + * and allocator. + * @param elem Returned value type, if any. + * @param pol Type of deletion policy. + * @param allocator The allocator to use (possibly default-constructed). + */ + explicit basic_sparse_set(const type_info &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) + : sparse{allocator}, + packed{allocator}, + descriptor{&elem}, + mode{pol}, + head{policy_to_head()} { + ENTT_ASSERT(traits_type::version_mask || mode != deletion_policy::in_place, "Policy does not support zero-sized versions"); + } + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_sparse_set(const basic_sparse_set &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sparse_set(basic_sparse_set &&other) noexcept + : sparse{std::move(other.sparse)}, + packed{std::move(other.packed)}, + descriptor{other.descriptor}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) + : sparse{std::move(other.sparse), allocator}, + packed{std::move(other.packed), allocator}, + descriptor{other.descriptor}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed"); + } + + /*! @brief Default destructor. */ + virtual ~basic_sparse_set() { + release_sparse_pages(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This sparse set. + */ + basic_sparse_set &operator=(const basic_sparse_set &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This sparse set. + */ + basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a sparse set is not allowed"); + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given sparse set. + * @param other Sparse set to exchange the content with. + */ + void swap(basic_sparse_set &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(descriptor, other.descriptor); + swap(mode, other.mode); + swap(head, other.head); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return packed.get_allocator(); + } + + /** + * @brief Returns the deletion policy of a sparse set. + * @return The deletion policy of the sparse set. + */ + [[nodiscard]] deletion_policy policy() const noexcept { + return mode; + } + + /** + * @brief Returns data on the free list whose meaning depends on the mode. + * @return Free list information that is mode dependent. + */ + [[nodiscard]] size_type free_list() const noexcept { + return head; + } + + /** + * @brief Sets data on the free list whose meaning depends on the mode. + * @param value Free list information that is mode dependent. + */ + void free_list(const size_type value) noexcept { + ENTT_ASSERT((mode == deletion_policy::swap_only) && !(value > packed.size()), "Invalid value"); + head = value; + } + + /** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + virtual void reserve(const size_type cap) { + packed.reserve(cap); + } + + /** + * @brief Returns the number of elements that a sparse set has currently + * allocated space for. + * @return Capacity of the sparse set. + */ + [[nodiscard]] virtual size_type capacity() const noexcept { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + virtual void shrink_to_fit() { + sparse_container_type other{sparse.get_allocator()}; + const auto len = sparse.size(); + size_type cnt{}; + + other.reserve(len); + + for(auto &&elem: std::as_const(packed)) { + if(elem != tombstone) { + if(const auto page = pos_to_page(entity_to_pos(elem)); sparse[page] != nullptr) { + if(const auto sz = page + 1u; sz > other.size()) { + other.resize(sz, nullptr); + } + + other[page] = std::exchange(sparse[page], nullptr); + + if(++cnt == len) { + // early exit due to lack of pages + break; + } + } + } + } + + release_sparse_pages(); + sparse.swap(other); + + 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 all pages have been allocated, nor that the + * internal packed array is be the same size. + * + * @return Extent of the sparse set. + */ + [[nodiscard]] size_type extent() const noexcept { + return sparse.size() * traits_type::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 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 noexcept { + return packed.empty(); + } + + /** + * @brief Checks whether a sparse set is fully packed. + * @return True if the sparse set is fully packed, false otherwise. + */ + [[nodiscard]] bool contiguous() const noexcept { + return (mode != deletion_policy::in_place) || (head == max_size); + } + + /** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ + [[nodiscard]] pointer data() const noexcept { + return packed.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `end()`. + * + * @return An iterator to the first entity of the sparse set. + */ + [[nodiscard]] iterator begin() const noexcept { + const auto pos = static_cast(packed.size()); + return iterator{packed, pos}; + } + + /*! @copydoc begin */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last entity of a sparse + * set. + */ + [[nodiscard]] iterator end() const noexcept { + return iterator{packed, {}}; + } + + /*! @copydoc end */ + [[nodiscard]] const_iterator cend() const noexcept { + return end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * 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 noexcept { + return std::make_reverse_iterator(end()); + } + + /*! @copydoc rbegin */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last entity of the + * reversed sparse set. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return std::make_reverse_iterator(begin()); + } + + /*! @copydoc rend */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return rend(); + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] const_iterator find(const entity_type entt) const noexcept { + return contains(entt) ? to_iterator(entt) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + const auto *elem = sparse_ptr(entt); + constexpr auto cap = traits_type::entity_mask; + constexpr auto mask = traits_type::to_integral(null) & ~cap; + // testing versions permits to avoid accessing the packed array + return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); + } + + /** + * @brief Returns the contained version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if present, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const noexcept { + const auto *elem = sparse_ptr(entt); + constexpr auto fallback = traits_type::to_version(tombstone); + return elem ? traits_type::to_version(*elem) : fallback; + } + + /** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const noexcept { + ENTT_ASSERT(contains(entt), "Set does not contain entity"); + return entity_to_pos(sparse_ref(entt)); + } + + /** + * @brief Returns the entity at specified location. + * @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 noexcept { + ENTT_ASSERT(pos < packed.size(), "Index out of bounds"); + return packed[pos]; + } + + /** + * @brief Returns the element assigned to an entity, if any. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior. + * + * @param entt A valid identifier. + * @return An opaque pointer to the element assigned to the entity, if any. + */ + [[nodiscard]] const void *value(const entity_type entt) const noexcept { + return get_at(index(entt)); + } + + /*! @copydoc value */ + [[nodiscard]] void *value(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).value(entt)); + } + + /** + * @brief Assigns an entity to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + * @param elem Optional opaque element to forward to mixins, if any. + * @return Iterator pointing to the emplaced element in case of success, the + * `end()` iterator otherwise. + */ + iterator push(const entity_type entt, const void *elem = nullptr) { + return try_emplace(entt, false, elem); + } + + /** + * @brief Assigns one or more entities to a sparse set. + * + * @warning + * Attempting to assign an entity that already belongs to the sparse set + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return Iterator pointing to the first element inserted in case of + * success, the `end()` iterator otherwise. + */ + template + iterator push(It first, It last) { + auto curr = end(); + + for(; first != last; ++first) { + curr = try_emplace(*first, true); + } + + return curr; + } + + /** + * @brief Bump the version number of an entity. + * + * @warning + * Attempting to bump the version of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the given identifier. + */ + version_type bump(const entity_type entt) { + auto &elem = sparse_ref(entt); + ENTT_ASSERT(entt != null && elem != tombstone, "Cannot set the required version"); + elem = traits_type::combine(traits_type::to_integral(elem), traits_type::to_integral(entt)); + packed[entity_to_pos(elem)] = entt; + return traits_type::to_version(entt); + } + + /** + * @brief Erases an entity from a sparse set. + * + * @warning + * Attempting to erase an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + */ + void erase(const entity_type entt) { + const auto it = to_iterator(entt); + pop(it, it + 1u); + } + + /** + * @brief Erases entities from a set. + * + * @sa erase + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void erase(It first, It last) { + if constexpr(std::is_same_v) { + pop(first, last); + } else { + for(; first != last; ++first) { + erase(*first); + } + } + } + + /** + * @brief Removes an entity from a sparse set if it exists. + * @param entt A valid identifier. + * @return True if the entity is actually removed, false otherwise. + */ + bool remove(const entity_type entt) { + return contains(entt) && (erase(entt), true); + } + + /** + * @brief Removes entities from a sparse set if they exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of entities actually removed. + */ + template + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v) { + while(first != last) { + while(first != last && !contains(*first)) { + ++first; + } + + const auto it = first; + + while(first != last && contains(*first)) { + ++first; + } + + count += static_cast(std::distance(it, first)); + erase(it, first); + } + } else { + for(; first != last; ++first) { + count += remove(*first); + } + } + + return count; + } + + /*! @brief Removes all tombstones from a sparse set. */ + void compact() { + if(mode == deletion_policy::in_place) { + size_type from = packed.size(); + size_type pos = std::exchange(head, max_size); + + for(; from && packed[from - 1u] == tombstone; --from) {} + + while(pos != max_size) { + if(const auto to = std::exchange(pos, entity_to_pos(packed[pos])); to < from) { + --from; + swap_or_move(from, to); + + packed[to] = packed[from]; + const auto elem = static_cast(to); + sparse_ref(packed[to]) = traits_type::combine(elem, traits_type::to_integral(packed[to])); + + for(; from && packed[from - 1u] == tombstone; --from) {} + } + } + + packed.erase(packed.begin() + static_cast(from), packed.end()); + } + } + + /** + * @brief Swaps two entities in a sparse set. + * + * For what it's worth, this function affects both the internal sparse array + * and the internal packed array. Users should not care of that anyway. + * + * @warning + * Attempting to swap entities that don't belong to the sparse set results + * in undefined behavior. + * + * @param lhs A valid identifier. + * @param rhs A valid identifier. + */ + void swap_elements(const entity_type lhs, const entity_type rhs) { + const auto from = index(lhs); + const auto to = index(rhs); + + // basic no-leak guarantee if swapping throws + swap_or_move(from, 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 length Number of elements to sort. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed"); + ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); + + algo(packed.rend() - static_cast(length), packed.rend(), std::move(compare), std::forward(args)...); + + for(size_type pos{}; pos < length; ++pos) { + auto curr = pos; + auto next = index(packed[curr]); + + while(curr != next) { + const auto idx = index(packed[next]); + const auto entt = packed[curr]; + + swap_or_move(next, idx); + const auto elem = static_cast(curr); + sparse_ref(entt) = traits_type::combine(elem, traits_type::to_integral(packed[curr])); + curr = std::exchange(next, idx); + } + } + } + + /** + * @brief Sort all elements according to the given comparison function. + * + * @sa sort_n + * + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size(); + sort_n(len, std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort entities according to their order in a range. + * + * Entities that are part of both the sparse set and the range are ordered + * internally according to the order they have in the range.
+ * All other entities goes to the end of the sparse set and there are no + * guarantees on their order. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return An iterator past the last of the elements actually shared. + */ + template + iterator sort_as(It first, It last) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == max_size), "Sorting with tombstones not allowed"); + const size_type len = (mode == deletion_policy::swap_only) ? head : packed.size(); + auto it = end() - static_cast(len); + + for(const auto other = end(); (it != other) && (first != last); ++first) { + if(const auto curr = *first; contains(curr)) { + if(const auto entt = *it; entt != curr) { + // basic no-leak guarantee (with invalid state) if swapping throws + swap_elements(entt, curr); + } + + ++it; + } + } + + return it; + } + + /*! @brief Clears a sparse set. */ + void clear() { + pop_all(); + // sanity check to avoid subtle issues due to storage classes + ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Returns a type info object for the value type, if any. + * @return A type info object for the value type, if any. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *descriptor; + } + + /*! @copydoc info */ + [[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept { + return info(); + } + + /** + * @brief Forwards variables to derived classes, if any. + * @tparam Type Type of the element to forward. + * @param value The element to forward. + */ + template + void bind(Type &&value) noexcept { + bind_any(forward_as_any(std::forward(value))); + } + +private: + sparse_container_type sparse; + packed_container_type packed; + const type_info *descriptor; + deletion_policy mode; + size_type head; +}; + +} // namespace entt + +#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/bit.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class storage_iterator final { + friend storage_iterator; + + using container_type = std::remove_const_t; + using alloc_traits = std::allocator_traits; + + using iterator_traits = std::iterator_traits, + typename alloc_traits::template rebind_traits::element_type>::const_pointer, + typename alloc_traits::template rebind_traits::element_type>::pointer>>; + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr storage_iterator() noexcept = default; + + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept + : payload{ref}, + offset{idx} {} + + template, typename = std::enable_if_t> + constexpr storage_iterator(const storage_iterator, Page> &other) noexcept + : storage_iterator{other.payload, other.offset} {} + + constexpr storage_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr storage_iterator operator++(int) noexcept { + const storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr storage_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr storage_iterator operator--(int) noexcept { + const storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr storage_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr storage_iterator operator+(const difference_type value) const noexcept { + storage_iterator copy = *this; + return (copy += value); + } + + constexpr storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + const auto pos = static_cast(index() - value); + return (*payload)[pos / Page][fast_mod(static_cast(pos), Page)]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + Container *payload; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class extended_storage_iterator final { + template + friend class extended_storage_iterator; + +public: + using iterator_type = It; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::forward_as_tuple(*std::declval()...))); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_storage_iterator() + : it{} {} + + constexpr extended_storage_iterator(iterator_type base, Other... other) + : it{base, other...} {} + + template && ...) && (std::is_constructible_v && ...)>> + constexpr extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} + + constexpr extended_storage_iterator &operator++() noexcept { + return ++std::get(it), (++std::get(it), ...), *this; + } + + constexpr extended_storage_iterator operator++(int) noexcept { + const extended_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {*std::get(it), *std::get(it)...}; + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return std::get(it); + } + + template + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; + +private: + std::tuple it; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @tparam Type Element type. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage: public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + using traits_type = component_traits; + + [[nodiscard]] auto &element_at(const std::size_t pos) const { + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + auto assure_at_least(const std::size_t pos) { + const auto idx = pos / traits_type::page_size; + + if(!(idx < payload.size())) { + auto curr = payload.size(); + allocator_type allocator{get_allocator()}; + payload.resize(idx + 1u, nullptr); + + ENTT_TRY { + for(const auto last = payload.size(); curr < last; ++curr) { + payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); + } + } + ENTT_CATCH { + payload.resize(curr); + ENTT_THROW; + } + } + + return payload[idx] + fast_mod(pos, traits_type::page_size); + } + + template + auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { + const auto it = base_type::try_emplace(entt, force_back); + + ENTT_TRY { + auto *elem = to_address(assure_at_least(static_cast(it.index()))); + entt::uninitialized_construct_using_allocator(elem, get_allocator(), std::forward(args)...); + } + ENTT_CATCH { + base_type::pop(it, it + 1u); + ENTT_THROW; + } + + return it; + } + + void shrink_to_size(const std::size_t sz) { + const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; + allocator_type allocator{get_allocator()}; + + for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { + if constexpr(traits_type::in_place_delete) { + if(base_type::data()[pos] != tombstone) { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } else { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } + + for(auto pos = from, last = payload.size(); pos < last; ++pos) { + alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size); + } + + payload.resize(from); + payload.shrink_to_fit(); + } + + void swap_at(const std::size_t lhs, const std::size_t rhs) { + using std::swap; + swap(element_at(lhs), element_at(rhs)); + } + + void move_to(const std::size_t lhs, const std::size_t rhs) { + auto &elem = element_at(lhs); + allocator_type allocator{get_allocator()}; + entt::uninitialized_construct_using_allocator(to_address(assure_at_least(rhs)), allocator, std::move(elem)); + alloc_traits::destroy(allocator, std::addressof(elem)); + } + +private: + [[nodiscard]] const void *get_at(const std::size_t pos) const final { + return std::addressof(element_at(pos)); + } + + void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { + static constexpr bool is_pinned_type = !(std::is_move_constructible_v && std::is_move_assignable_v); + // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy + ENTT_ASSERT((from + 1u) && !is_pinned_type, "Pinned type"); + + if constexpr(!is_pinned_type) { + if constexpr(traits_type::in_place_delete) { + (base_type::operator[](to) == tombstone) ? move_to(from, to) : swap_at(from, to); + } else { + swap_at(from, to); + } + } + } + +protected: + /** + * @brief Erases entities from a storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + void pop(underlying_iterator first, underlying_iterator last) override { + for(allocator_type allocator{get_allocator()}; first != last; ++first) { + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); + + if constexpr(traits_type::in_place_delete) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(elem)); + } else { + auto &other = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); + alloc_traits::destroy(allocator, std::addressof(other)); + base_type::swap_and_pop(first); + } + } + } + + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + allocator_type allocator{get_allocator()}; + + for(auto first = base_type::begin(); !(first.index() < 0); ++first) { + if constexpr(traits_type::in_place_delete) { + if(*first != tombstone) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } else { + base_type::swap_and_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } + } + + /** + * @brief Assigns an entity to a storage. + * @param entt A valid identifier. + * @param value Optional opaque value. + * @param force_back Force back insertion. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { + if(value != nullptr) { + if constexpr(std::is_copy_constructible_v) { + return emplace_element(entt, force_back, *static_cast(value)); + } else { + return base_type::end(); + } + } else { + if constexpr(std::is_default_constructible_v) { + return emplace_element(entt, force_back); + } else { + return base_type::end(); + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Base type. */ + using base_type = underlying_type; + /*! @brief Element type. */ + using element_type = Type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = element_type; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Pointer type to contained elements. */ + using pointer = typename container_type::pointer; + /*! @brief Constant pointer type to contained elements. */ + using const_pointer = typename alloc_traits::template rebind_traits::const_pointer; + /*! @brief Random access iterator type. */ + using iterator = internal::storage_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::storage_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + /*! @brief Storage deletion policy. */ + static constexpr deletion_policy storage_policy{traits_type::in_place_delete}; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), storage_policy, allocator}, + payload{allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_storage(const basic_storage &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{static_cast(other)}, + payload{std::move(other.payload)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) + : base_type{static_cast(other), allocator}, + payload{std::move(other.payload), allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed"); + } + + /*! @brief Default destructor. */ + // NOLINTNEXTLINE(bugprone-exception-escape) + ~basic_storage() override { + shrink_to_size(0u); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This storage. + */ + basic_storage &operator=(const basic_storage &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a storage is not allowed"); + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_storage &other) noexcept { + using std::swap; + swap(payload, other.payload); + base_type::swap(other); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return payload.get_allocator(); + } + + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) override { + if(cap != 0u) { + base_type::reserve(cap); + assure_at_least(cap - 1u); + } + } + + /** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ + [[nodiscard]] size_type capacity() const noexcept override { + return payload.size() * traits_type::page_size; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() override { + base_type::shrink_to_fit(); + shrink_to_size(base_type::size()); + } + + /** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ + [[nodiscard]] const_pointer raw() const noexcept { + return payload.data(); + } + + /*! @copydoc raw */ + [[nodiscard]] pointer raw() noexcept { + return payload.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + const auto pos = static_cast(base_type::size()); + return const_iterator{&payload, pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + const auto pos = static_cast(base_type::size()); + return iterator{&payload, pos}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return const_iterator{&payload, {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return iterator{&payload, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * 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 noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { + return element_at(base_type::index(entt)); + } + + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { + return std::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { + return std::forward_as_tuple(get(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ + template + value_type &emplace(const entity_type entt, Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + const auto it = emplace_element(entt, false, Type{std::forward(args)...}); + return element_at(static_cast(it.index())); + } else { + const auto it = emplace_element(entt, false, std::forward(args)...); + return element_at(static_cast(it.index())); + } + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the updated instance. + */ + template + value_type &patch(const entity_type entt, Func &&...func) { + const auto idx = base_type::index(entt); + auto &elem = element_at(idx); + (std::forward(func)(elem), ...); + return elem; + } + + /** + * @brief Assigns one or more entities to a storage and constructs their + * objects from a given instance. + * + * @warning + * Attempting to assign an entity that already belongs to the storage + * results in undefined behavior. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the object to construct. + * @return Iterator pointing to the first element inserted, if any. + */ + template + iterator insert(It first, It last, const value_type &value = {}) { + for(; first != last; ++first) { + emplace_element(*first, true, value); + } + + return begin(); + } + + /** + * @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. + * @return Iterator pointing to the first element inserted, if any. + */ + template::value_type, value_type>>> + iterator insert(EIt first, EIt last, CIt from) { + for(; first != last; ++first, ++from) { + emplace_element(*first, true, *from); + } + + return begin(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its element. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return iterable{{base_type::begin(), begin()}, {base_type::end(), end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return const_iterable{{base_type::cbegin(), cbegin()}, {base_type::cend(), cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return reverse_iterable{{base_type::rbegin(), rbegin()}, {base_type::rend(), rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return const_reverse_iterable{{base_type::crbegin(), crbegin()}, {base_type::crend(), crend()}}; + } + +private: + container_type payload; +}; + +/*! @copydoc basic_storage */ +template +class basic_storage::page_size == 0u>> + : public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using traits_type = component_traits; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Base type. */ + using base_type = basic_sparse_set>; + /*! @brief Element type. */ + using element_type = Type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = void; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + /*! @brief Storage deletion policy. */ + static constexpr deletion_policy storage_policy{traits_type::in_place_delete}; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), storage_policy, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_storage(const basic_storage &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) + : base_type{std::move(other), allocator} {} + + /*! @brief Default destructor. */ + ~basic_storage() override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This storage. + */ + basic_storage &operator=(const basic_storage &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + // std::allocator has no cross constructors (waiting for C++20) + if constexpr(std::is_void_v && !std::is_constructible_v) { + return allocator_type{}; + } else { + return allocator_type{base_type::get_allocator()}; + } + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + } + + /** + * @brief Returns an empty tuple. + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + return std::tuple{}; + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @param entt A valid identifier. + */ + void emplace(const entity_type entt) { + base_type::try_emplace(entt, false); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns entities to a storage. + * @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) { + for(; first != last; ++first) { + base_type::try_emplace(*first, true); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return iterable{base_type::begin(), base_type::end()}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return const_iterable{base_type::cbegin(), base_type::cend()}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return reverse_iterable{{base_type::rbegin()}, {base_type::rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return const_reverse_iterable{{base_type::crbegin()}, {base_type::crend()}}; + } +}; + +/** + * @brief Swap-only entity storage specialization. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage + : public basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using underlying_iterator = typename basic_sparse_set::basic_iterator; + using traits_type = entt_traits; + + auto from_placeholder() noexcept { + const auto entt = traits_type::combine(static_cast(placeholder), {}); + ENTT_ASSERT(entt != null, "No more entities available"); + placeholder += static_cast(entt != null); + return entt; + } + + auto next() noexcept { + entity_type entt = from_placeholder(); + + while(base_type::current(entt) != traits_type::to_version(tombstone) && entt != null) { + entt = from_placeholder(); + } + + return entt; + } + +protected: + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + base_type::pop_all(); + placeholder = {}; + } + + /** + * @brief Assigns an entity to a storage. + * @param hint A valid identifier. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { + return base_type::find(generate(hint)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Base type. */ + using base_type = basic_sparse_set; + /*! @brief Element type. */ + using element_type = Entity; + /*! @brief Type of the objects assigned to entities. */ + using value_type = void; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_iterable = iterable_adaptor>; + /*! @brief Storage deletion policy. */ + static constexpr deletion_policy storage_policy = deletion_policy::swap_only; + + /*! @brief Default constructor. */ + basic_storage() + : basic_storage{allocator_type{}} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_storage(const allocator_type &allocator) + : base_type{type_id(), storage_policy, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_storage(const basic_storage &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + basic_storage(basic_storage &&other) noexcept + : base_type{static_cast(other)}, + placeholder{other.placeholder} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + basic_storage(basic_storage &&other, const allocator_type &allocator) + : base_type{static_cast(other), allocator}, + placeholder{other.placeholder} {} + + /*! @brief Default destructor. */ + ~basic_storage() override = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This storage. + */ + basic_storage &operator=(const basic_storage &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + placeholder = other.placeholder; + base_type::operator=(std::move(other)); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_storage &other) noexcept { + using std::swap; + swap(placeholder, other.placeholder); + base_type::swap(other); + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + } + + /** + * @brief Returns an empty tuple. + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + return std::tuple{}; + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + entity_type generate() { + const auto len = base_type::free_list(); + const auto entt = (len == base_type::size()) ? next() : base_type::data()[len]; + return *base_type::try_emplace(entt, true); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * + * If the requested identifier isn't in use, the suggested one is used. + * Otherwise, a new identifier is returned. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type generate(const entity_type hint) { + if(hint != null && hint != tombstone) { + if(const auto curr = traits_type::construct(traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone || !(base_type::index(curr) < base_type::free_list())) { + return *base_type::try_emplace(hint, true); + } + } + + return generate(); + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable forward iterator. + * @param first An iterator to the first element of the range to generate. + * @param last An iterator past the last element of the range to generate. + */ + template + void generate(It first, It last) { + for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) { + *first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true); + } + + for(; first != last; ++first) { + *first = *base_type::try_emplace(next(), true); + } + } + + /** + * @brief Updates a given identifier. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + (std::forward(func)(), ...); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return std::as_const(*this).each(); + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + const auto it = base_type::cend(); + const auto offset = static_cast(base_type::free_list()); + return const_iterable{it - offset, it}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return std::as_const(*this).reach(); + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + const auto it = base_type::crbegin(); + const auto offset = static_cast(base_type::free_list()); + return const_reverse_iterable{it, it + offset}; + } + + /** + * @brief Sets the starting identifier for generation. + * + * The version is ignored, regardless of the value. + * + * @param hint A valid identifier. + */ + void start_from(const entity_type hint) { + placeholder = static_cast(traits_type::to_entity(hint)); + } + +private: + size_type placeholder{}; +}; + +} // namespace entt + +#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/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +// NOLINTNEXTLINE(misc-redundant-expression) +static constexpr bool tombstone_check_v = ((sizeof...(Type) == 1u) && ... && (Type::storage_policy == deletion_policy::in_place)); + +template +const Type *view_placeholder() { + static_assert(std::is_same_v>, Type>, "Unexpected type"); + static const Type placeholder{}; + return &placeholder; +} + +template +[[nodiscard]] bool all_of(It first, const It last, const Entity entt) noexcept { + for(; (first != last) && (*first)->contains(entt); ++first) {} + return first == last; +} + +template +[[nodiscard]] bool none_of(It first, const It last, const Entity entt) noexcept { + for(; (first != last) && !(*first)->contains(entt); ++first) {} + return first == last; +} + +template +[[nodiscard]] bool fully_initialized(It first, const It last, const std::remove_pointer_t::value_type> *placeholder) noexcept { + for(; (first != last) && *first != placeholder; ++first) {} + return first == last; +} + +template +[[nodiscard]] Result view_pack(const View &view, const Other &other, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence) { + Result elem{}; + // friend-initialization, avoid multiple calls to refresh + elem.pools = {view.template storage()..., other.template storage()...}; + auto filter_or_placeholder = [placeholder = elem.placeholder](auto *value) { return (value == nullptr) ? placeholder : value; }; + elem.filter = {filter_or_placeholder(view.template storage())..., filter_or_placeholder(other.template storage())...}; + elem.refresh(); + return elem; +} + +template +class view_iterator final { + template + friend class extended_view_iterator; + + using iterator_type = typename Type::const_iterator; + using iterator_traits = std::iterator_traits; + + [[nodiscard]] bool valid(const typename iterator_traits::value_type entt) const noexcept { + return (!Checked || (entt != tombstone)) + && ((Get == 1u) || (internal::all_of(pools.begin(), pools.begin() + index, entt) && internal::all_of(pools.begin() + index + 1, pools.end(), entt))) + && ((Exclude == 0u) || internal::none_of(filter.begin(), filter.end(), entt)); + } + + void seek_next() { + for(constexpr iterator_type sentinel{}; it != sentinel && !valid(*it); ++it) {} + } + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::forward_iterator_tag; + + constexpr view_iterator() noexcept + : it{}, + pools{}, + filter{}, + index{} {} + + view_iterator(iterator_type first, std::array value, std::array excl, const std::size_t idx) noexcept + : it{first}, + pools{value}, + filter{excl}, + index{static_cast(idx)} { + ENTT_ASSERT((Get != 1u) || (Exclude != 0u) || pools[0u]->policy() == deletion_policy::in_place, "Non in-place storage view iterator"); + seek_next(); + } + + view_iterator &operator++() noexcept { + ++it; + seek_next(); + return *this; + } + + view_iterator operator++(int) noexcept { + const view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return &*it; + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; + +private: + iterator_type it; + std::array pools; + std::array filter; + difference_type index; +}; + +template +[[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +class extended_view_iterator final { + template + [[nodiscard]] auto dereference(std::index_sequence) const noexcept { + return std::tuple_cat(std::make_tuple(*it), static_cast(const_cast *>(std::get(it.pools)))->get_as_tuple(*it)...); + } + +public: + using iterator_type = It; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_view_iterator() + : it{} {} + + extended_view_iterator(iterator_type from) + : it{from} {} + + extended_view_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_view_iterator operator++(int) noexcept { + const extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return dereference(std::index_sequence_for{}); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief View implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + * + * @b Important + * + * View iterators aren't invalidated if: + * + * * New elements are added to the storage iterated by the view. + * * The entity currently returned is modified (for example, elements are added + * or removed from it). + * * The entity currently returned is destroyed. + * + * In all other cases, modifying the storage iterated by a view in any way can + * invalidate all iterators. + */ +template +class basic_view; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Checked True to enable the tombstone check, false otherwise. + * @tparam Get Number of storage iterated by the view. + * @tparam Exclude Number of storage used to filter the view. + */ +template +class basic_common_view { + static_assert(std::is_same_v>, Type>, "Unexpected type"); + + template + friend Return internal::view_pack(const View &, const Other &, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence); + + [[nodiscard]] auto offset() const noexcept { + ENTT_ASSERT(index != Get, "Invalid view"); + return (pools[index]->policy() == deletion_policy::swap_only) ? pools[index]->free_list() : pools[index]->size(); + } + + void unchecked_refresh() noexcept { + index = 0u; + + if constexpr(Get > 1u) { + for(size_type pos{1u}; pos < Get; ++pos) { + if(pools[pos]->size() < pools[index]->size()) { + index = pos; + } + } + } + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_common_view() noexcept { + for(size_type pos{}, last = filter.size(); pos < last; ++pos) { + filter[pos] = placeholder; + } + } + + basic_common_view(std::array value, std::array excl) noexcept + : pools{value}, + filter{excl}, + index{Get} { + unchecked_refresh(); + } + + [[nodiscard]] const Type *pool_at(const std::size_t pos) const noexcept { + return pools[pos]; + } + + void pool_at(const std::size_t pos, const Type *elem) noexcept { + ENTT_ASSERT(elem != nullptr, "Unexpected element"); + pools[pos] = elem; + refresh(); + } + + [[nodiscard]] const Type *filter_at(const std::size_t pos) const noexcept { + return (filter[pos] == placeholder) ? nullptr : filter[pos]; + } + + void filter_at(const std::size_t pos, const Type *elem) noexcept { + ENTT_ASSERT(elem != nullptr, "Unexpected element"); + filter[pos] = elem; + } + + [[nodiscard]] bool none_of(const typename Type::entity_type entt) const noexcept { + return internal::none_of(filter.begin(), filter.end(), entt); + } + + void use(const std::size_t pos) noexcept { + index = (index != Get) ? pos : Get; + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Forward iterator type. */ + using iterator = internal::view_iterator; + + /*! @brief Updates the internal leading view if required. */ + void refresh() noexcept { + size_type pos = static_cast(index != Get) * Get; + for(; pos < Get && pools[pos] != nullptr; ++pos) {} + + if(pos == Get) { + unchecked_refresh(); + } + } + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return (index != Get) ? pools[index] : nullptr; + } + + /** + * @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 noexcept { + return (index != Get) ? offset() : size_type{}; + } + + /** + * @brief Returns an iterator 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 noexcept { + return (index != Get) ? iterator{pools[index]->end() - static_cast(offset()), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return (index != Get) ? iterator{pools[index]->end(), pools, filter, index} : iterator{}; + } + + /** + * @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 noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + if(index != Get) { + auto it = pools[index]->rbegin(); + const auto last = it + static_cast(offset()); + for(const auto idx = static_cast(index); it != last && !(internal::all_of(pools.begin(), pools.begin() + idx, *it) && internal::all_of(pools.begin() + idx + 1, pools.end(), *it) && internal::none_of(filter.begin(), filter.end(), *it)); ++it) {} + return it == last ? null : *it; + } + + return null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return contains(entt) ? iterator{pools[index]->find(entt), pools, filter, index} : end(); + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (index != Get) && internal::fully_initialized(filter.begin(), filter.end(), placeholder); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return (index != Get) + && internal::all_of(pools.begin(), pools.end(), entt) + && internal::none_of(filter.begin(), filter.end(), entt) + && pools[index]->index(entt) < offset(); + } + +private: + std::array pools{}; + std::array filter{}; + const common_type *placeholder{internal::view_placeholder()}; + size_type index{Get}; +}; + +/** + * @brief General purpose view. + * + * This view visits all entities that are at least in the given storage. During + * initialization, it also looks at the number of elements available for each + * storage and uses the smallest set in order to get a performance boost. + * + * @sa basic_view + * + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template +class basic_view, exclude_t, std::enable_if_t<(sizeof...(Get) != 0u)>> + : public basic_common_view, internal::tombstone_check_v, sizeof...(Get), sizeof...(Exclude)> { + using base_type = basic_common_view, internal::tombstone_check_v, sizeof...(Get), sizeof...(Exclude)>; + + template + using element_at = type_list_element_t>; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + [[nodiscard]] auto get(const typename base_type::entity_type entt, std::index_sequence) const noexcept { + return std::tuple_cat(storage()->get_as_tuple(entt)...); + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Curr == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return storage()->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func &func, std::index_sequence) const { + for(const auto curr: storage()->each()) { + if(const auto entt = std::get<0>(curr); (!internal::tombstone_check_v || (entt != tombstone)) && ((Curr == Index || base_type::pool_at(Index)->contains(entt)) && ...) && base_type::none_of(entt)) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(curr)...)); + } + } + } + } + + template + void pick_and_each(Func &func, std::index_sequence seq) const { + if(const auto *view = base_type::handle(); view != nullptr) { + ((view == base_type::pool_at(Index) ? each(func, seq) : void()), ...); + } + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Forward iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Iterable view type. */ + using iterable = iterable_adaptor>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(Get &...value, Exclude &...excl) noexcept + : base_type{{&value...}, {&excl...}} { + } + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(std::tuple value, std::tuple excl = {}) noexcept + : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} + + /** + * @brief Forces a view to use a given element to drive iterations + * @tparam Type Type of element to use to drive iterations. + */ + template + void use() noexcept { + use>(); + } + + /** + * @brief Forces a view to use a given element to drive iterations + * @tparam Index Index of the element to use to drive iterations. + */ + template + void use() noexcept { + base_type::use(Index); + } + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + if constexpr(Index < sizeof...(Get)) { + return static_cast *>(const_cast> *>(base_type::pool_at(Index))); + } else { + return static_cast *>(const_cast> *>(base_type::filter_at(Index - sizeof...(Get)))); + } + } + + /** + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + storage>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + static_assert(std::is_convertible_v &>, "Unexpected type"); + + if constexpr(Index < sizeof...(Get)) { + base_type::pool_at(Index, &elem); + } else { + base_type::filter_at(Index - sizeof...(Get), &elem); + } + } + + /** + * @brief Returns the elements assigned to the given entity. + * @param entt A valid identifier. + * @return The elements assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return get(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Type Type of the element to get. + * @tparam Other Other types of elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + return get, index_of...>(entt); + } + + /** + * @brief Returns the elements assigned to the given entity. + * @tparam Index Indexes of the elements to get. + * @param entt A valid identifier. + * @return The elements assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return get(entt, std::index_sequence_for{}); + } else if constexpr(sizeof...(Index) == 1) { + return (storage()->get(entt), ...); + } else { + return std::tuple_cat(storage()->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and elements and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + pick_and_each(func, std::index_sequence_for{}); + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty elements. The _constness_ of the + * elements is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + return iterable{base_type::begin(), base_type::end()}; + } + + /** + * @brief Combines a view and a storage in _more specific_ view. + * @tparam OGet Type of storage to combine the view with. + * @param other The storage for the type to combine the view with. + * @return A more specific view. + */ + template + [[nodiscard]] std::enable_if_t, basic_view, exclude_t>> operator|(OGet &other) const noexcept { + return *this | basic_view, exclude_t<>>{other}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Element list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Policy Storage policy. + */ +template +class basic_storage_view { + static_assert(std::is_same_v>, Type>, "Unexpected type"); + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_storage_view() noexcept = default; + + basic_storage_view(const Type *value) noexcept + : leading{value} { + ENTT_ASSERT(leading->policy() == Policy, "Unexpected storage policy"); + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename common_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Random access iterator type. */ + using iterator = std::conditional_t, typename common_type::iterator>; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::conditional_t; + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Returns the number of entities that have the given element. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return Number of entities that have the given element. + */ + template + [[nodiscard]] std::enable_if_t size() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->size() : size_type{}; + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return leading ? leading->free_list() : size_type{}; + } + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return Estimated number of entities iterated by the view. + */ + template + [[nodiscard]] std::enable_if_t size_hint() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Checks whether a view is empty. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return True if the view is empty, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t empty() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return !leading || leading->empty(); + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return !leading || (leading->free_list() == 0u); + } + } + + /** + * @brief Returns an iterator 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 noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->begin() : iterator{}; + } else if constexpr(Policy == deletion_policy::swap_only) { + return leading ? (leading->end() - static_cast(leading->free_list())) : iterator{}; + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + return leading ? iterator{leading->begin(), {leading}, {}, 0u} : iterator{}; + } + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) { + return leading ? leading->end() : iterator{}; + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + return leading ? iterator{leading->end(), {leading}, {}, 0u} : iterator{}; + } + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * If the view is empty, the returned iterator will be equal to `rend()`. + * + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return An iterator to the first entity of the reversed view. + */ + template + [[nodiscard]] std::enable_if_t rbegin() const noexcept { + return leading ? leading->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * @tparam Pol Dummy template parameter used for sfinae purposes only. + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + template + [[nodiscard]] std::enable_if_t rend() const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->rend() : reverse_iterator{}; + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return leading ? (leading->rbegin() + static_cast(leading->free_list())) : reverse_iterator{}; + } + } + + /** + * @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 noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return empty() ? null : *leading->begin(); + } else if constexpr(Policy == deletion_policy::swap_only) { + return empty() ? null : *(leading->end() - static_cast(leading->free_list())); + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + const auto it = begin(); + return (it == end()) ? null : *it; + } + } + + /** + * @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 noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::swap_only) { + return empty() ? null : *leading->rbegin(); + } else { + static_assert(Policy == deletion_policy::in_place, "Unexpected storage policy"); + + if(leading) { + auto it = leading->rbegin(); + const auto last = leading->rend(); + for(; (it != last) && (*it == tombstone); ++it) {} + return it == last ? null : *it; + } + + return null; + } + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop) { + return leading ? leading->find(entt) : iterator{}; + } else if constexpr(Policy == deletion_policy::swap_only) { + const auto it = leading ? leading->find(entt) : iterator{}; + return leading && (static_cast(it.index()) < leading->free_list()) ? it : iterator{}; + } else { + return leading ? iterator{leading->find(entt), {leading}, {}, 0u} : iterator{}; + } + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (leading != nullptr); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + if constexpr(Policy == deletion_policy::swap_and_pop || Policy == deletion_policy::in_place) { + return leading && leading->contains(entt); + } else { + static_assert(Policy == deletion_policy::swap_only, "Unexpected storage policy"); + return leading && leading->contains(entt) && (leading->index(entt) < leading->free_list()); + } + } + +private: + const common_type *leading{}; +}; + +/** + * @brief Storage view specialization. + * + * This specialization offers a boost in terms of performance. It can access the + * underlying data structure directly and avoid superfluous checks. + * + * @sa basic_view + * + * @tparam Get Type of storage iterated by the view. + */ +template +class basic_view, exclude_t<>> + : public basic_storage_view { + using base_type = basic_storage_view; + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Random access iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = typename base_type::reverse_iterator; + /*! @brief Iterable view type. */ + using iterable = std::conditional_t>, decltype(std::declval().each())>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(Get &value) noexcept + : base_type{&value} { + } + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(std::tuple value, std::tuple<> = {}) noexcept + : basic_view{std::get<0>(value)} {} + + /** + * @brief Returns the storage for a given element type, if any. + * @tparam Type Type of element of which to return the storage. + * @return The storage for the given element type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(std::is_same_v, typename Get::element_type>, "Invalid element type"); + return storage<0>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(Index == 0u, "Index out of bounds"); + return static_cast(const_cast *>(base_type::handle())); + } + + /** + * @brief Assigns a storage to a view. + * @param elem A storage to assign to the view. + */ + void storage(Get &elem) noexcept { + storage<0>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Get &elem) noexcept { + static_assert(Index == 0u, "Index out of bounds"); + *this = basic_view{elem}; + } + + /** + * @brief Returns a pointer to the underlying storage. + * @return A pointer to the underlying storage. + */ + [[nodiscard]] Get *operator->() const noexcept { + return storage(); + } + + /** + * @brief Returns the element assigned to the given entity. + * @param entt A valid identifier. + * @return The element assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return storage()->get(entt); + } + + /** + * @brief Returns the element assigned to the given entity. + * @tparam Elem Type of the element to get. + * @param entt A valid identifier. + * @return The element assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + static_assert(std::is_same_v, typename Get::element_type>, "Invalid element type"); + return get<0>(entt); + } + + /** + * @brief Returns the element assigned to the given entity. + * @tparam Index Index of the element to get. + * @param entt A valid identifier. + * @return The element assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return storage()->get_as_tuple(entt); + } else { + return storage()->get(entt); + } + } + + /** + * @brief Iterates entities and elements and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &); + * void(typename Type &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + for(const auto pack: each()) { + std::apply(func, pack); + } + } else if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) { + if constexpr(std::is_void_v) { + for(size_type pos = base_type::size(); pos; --pos) { + func(); + } + } else { + if(const auto len = static_cast(base_type::size()); len != 0) { + for(auto last = storage()->end(), first = last - len; first != last; ++first) { + func(*first); + } + } + } + } else { + static_assert(Get::storage_policy == deletion_policy::in_place, "Unexpected storage policy"); + + for(const auto pack: each()) { + std::apply([&func](const auto, auto &&...elem) { func(std::forward(elem)...); }, pack); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its element if it's a non-empty one. The _constness_ of + * the element is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) { + return base_type::handle() ? storage()->each() : iterable{}; + } else { + static_assert(Get::storage_policy == deletion_policy::in_place, "Unexpected storage policy"); + return iterable{base_type::begin(), base_type::end()}; + } + } + + /** + * @brief Combines a view and a storage in _more specific_ view. + * @tparam OGet Type of storage to combine the view with. + * @param other The storage for the type to combine the view with. + * @return A more specific view. + */ + template + [[nodiscard]] std::enable_if_t, basic_view, exclude_t<>>> operator|(OGet &other) const noexcept { + return *this | basic_view, exclude_t<>>{other}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Element list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for<>{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Type &...storage) -> basic_view, exclude_t<>>; + +/** + * @brief Deduction guide. + * @tparam Get Types of elements iterated by the view. + * @tparam Exclude Types of elements used to filter the view. + */ +template +basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; + +} // namespace entt + +#endif + +// #include "graph/adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template> +class adjacency_matrix; + +template> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + + void find_next() noexcept { + for(; pos != last && !it[static_cast(pos)]; pos += offset) {} + } + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept = default; + + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + find_next(); + } + + constexpr edge_iterator &operator++() noexcept { + pos += offset; + find_next(); + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + const edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it{}; + size_type vert{}; + size_type pos{}; + size_type last{}; + size_type offset{}; +}; + +template +[[nodiscard]] constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out-edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In-edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /*! @brief Default copy constructor. */ + adjacency_matrix(const adjacency_matrix &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /*! @brief Default move constructor. */ + adjacency_matrix(adjacency_matrix &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{other.vert} {} + + /*! @brief Default destructor. */ + ~adjacency_matrix() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) noexcept { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Returns true if an adjacency matrix is empty, false otherwise. + * + * @warning + * Potentially expensive, try to avoid it on hot paths. + * + * @return True if the adjacency matrix is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + const auto iterable = edges(); + return (iterable.begin() == iterable.end()); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out-edges of a vertex. + * @param vertex The vertex of which to return all out-edges. + * @return An iterable object to visit all out-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in-edges of a vertex. + * @param vertex The vertex of which to return all in-edges. + * @return An iterable object to visit all in-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "graph/dot.hpp" +#ifndef ENTT_GRAPH_DOT_HPP +#define ENTT_GRAPH_DOT_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @tparam Writer Vertex decorator type. + * @param out A standard output stream. + * @param graph The graph to output. + * @param writer Vertex decorator object. + */ +template +void dot(std::ostream &out, const Graph &graph, Writer writer) { + static_assert(std::is_base_of_v, "Invalid graph category"); + + if constexpr(std::is_same_v) { + out << "graph{"; + } else { + out << "digraph{"; + } + + for(auto &&vertex: graph.vertices()) { + out << vertex << "["; + writer(out, vertex); + out << "];"; + } + + for(auto [lhs, rhs]: graph.edges()) { + if constexpr(std::is_same_v) { + out << lhs << "--" << rhs << ";"; + } else { + out << lhs << "->" << rhs << ";"; + } + } + + out << "}"; +} + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @param out A standard output stream. + * @param graph The graph to output. + */ +template +void dot(std::ostream &out, const Graph &graph) { + return dot(out, graph, [](auto &&...) {}); +} + +} // namespace entt + +#endif + +// #include "graph/flow.hpp" +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator> +class dense_set; + +template +class basic_table; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Element types. + */ +template +using table = basic_table...>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits::max)(); + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + const dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + const dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept = default; + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return (offset = it[static_cast(offset)].next), *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + const dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + const auto idx = static_cast(offset); + return {it[idx].element.first, it[idx].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_map_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_map() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses a given element with bounds checking. + * @tparam Other Type of the key of an element to find. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/bit.hpp" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_set_placeholder_position = (std::numeric_limits::max)(); + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + const dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + const dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept = default; + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[static_cast(offset)].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + const dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[static_cast(offset)].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_set_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_set_placeholder_position; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) { + if(packed.second()(packed.first()[offset].second, value)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) { + if(packed.second()(packed.first()[offset].second, value)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_set() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array 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 noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + + void find_next() noexcept { + for(; pos != last && !it[static_cast(pos)]; pos += offset) {} + } + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept = default; + + // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + find_next(); + } + + constexpr edge_iterator &operator++() noexcept { + pos += offset; + find_next(); + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + const edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it{}; + size_type vert{}; + size_type pos{}; + size_type last{}; + size_type offset{}; +}; + +template +[[nodiscard]] constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out-edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In-edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} { + } + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /*! @brief Default copy constructor. */ + adjacency_matrix(const adjacency_matrix &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /*! @brief Default move constructor. */ + adjacency_matrix(adjacency_matrix &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{other.vert} {} + + /*! @brief Default destructor. */ + ~adjacency_matrix() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) noexcept { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Returns true if an adjacency matrix is empty, false otherwise. + * + * @warning + * Potentially expensive, try to avoid it on hot paths. + * + * @return True if the adjacency matrix is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + const auto iterable = edges(); + return (iterable.begin() == iterable.end()); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out-edges of a vertex. + * @param vertex The vertex of which to return all out-edges. + * @return An iterable object to visit all out-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in-edges of a vertex. + * @param vertex The vertex of which to return all in-edges. + * @return An iterable object to visit all in-edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class for creating task graphs. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_flow { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; + using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; + using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; + using adjacency_matrix_type = adjacency_matrix>; + + void emplace(const id_type res, const bool is_rw) { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + + if(!deps.contains(res) && sync_on != vertices.size()) { + deps[res].emplace_back(sync_on, true); + } + + deps[res].emplace_back(index.first(), is_rw); + } + + void setup_graph(adjacency_matrix_type &matrix) const { + for(const auto &elem: deps) { + const auto last = elem.second.cend(); + auto it = elem.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + matrix.insert(curr->first, it->first); + } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + matrix.insert(it->first, next->first); + } + } else { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + } + } + } + } else { + // ro item (first iteration only) + if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(it->first, next->first); + } + } else { + it = last; + } + } + } + } + } + + void transitive_closure(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { + matrix.insert(vi, vj); + } + } + } + } + } + + void transitive_reduction(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(std::size_t vk{}; vk < length; ++vk) { + if(matrix.contains(vj, vk)) { + matrix.erase(vi, vk); + } + } + } + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable task list. */ + using iterable = iterable_adaptor; + /*! @brief Adjacency matrix type. */ + using graph_type = adjacency_matrix_type; + + /*! @brief Default constructor. */ + basic_flow() + : basic_flow{allocator_type{}} {} + + /** + * @brief Constructs a flow builder with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_flow(const allocator_type &allocator) + : index{0u, allocator}, + vertices{allocator}, + deps{allocator} {} + + /*! @brief Default copy constructor. */ + basic_flow(const basic_flow &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_flow(const basic_flow &other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{other.vertices, allocator}, + deps{other.deps, allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default move constructor. */ + basic_flow(basic_flow &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_flow(basic_flow &&other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{std::move(other.vertices), allocator}, + deps{std::move(other.deps), allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default destructor. */ + ~basic_flow() = default; + + /** + * @brief Default copy assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(const basic_flow &) = default; + + /** + * @brief Default move assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(basic_flow &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given flow builder. + * @param other Flow builder to exchange the content with. + */ + void swap(basic_flow &other) noexcept { + using std::swap; + std::swap(index, other.index); + std::swap(vertices, other.vertices); + std::swap(deps, other.deps); + std::swap(sync_on, other.sync_on); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{index.second()}; + } + + /** + * @brief Returns the identifier at specified location. + * @param pos Position of the identifier to return. + * @return The requested identifier. + */ + [[nodiscard]] id_type operator[](const size_type pos) const { + return vertices.cbegin()[static_cast(pos)]; + } + + /*! @brief Clears the flow builder. */ + void clear() noexcept { + index.first() = {}; + vertices.clear(); + deps.clear(); + sync_on = {}; + } + + /** + * @brief Returns true if a flow builder contains no tasks, false otherwise. + * @return True if the flow builder contains no tasks, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return vertices.empty(); + } + + /** + * @brief Returns the number of tasks. + * @return The number of tasks. + */ + [[nodiscard]] size_type size() const noexcept { + return vertices.size(); + } + + /** + * @brief Binds a task to a flow builder. + * @param value Task identifier. + * @return This flow builder. + */ + basic_flow &bind(const id_type value) { + sync_on += (sync_on == vertices.size()); + const auto it = vertices.emplace(value).first; + index.first() = size_type(it - vertices.begin()); + return *this; + } + + /** + * @brief Turns the current task into a sync point. + * @return This flow builder. + */ + basic_flow &sync() { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + sync_on = index.first(); + + for(const auto &elem: deps) { + elem.second.emplace_back(sync_on, true); + } + + return *this; + } + + /** + * @brief Assigns a resource to the current task with a given access mode. + * @param res Resource identifier. + * @param is_rw Access mode. + * @return This flow builder. + */ + basic_flow &set(const id_type res, bool is_rw = false) { + emplace(res, is_rw); + return *this; + } + + /** + * @brief Assigns a read-only resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &ro(const id_type res) { + emplace(res, false); + return *this; + } + + /** + * @brief Assigns a range of read-only resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + ro(It first, It last) { + for(; first != last; ++first) { + emplace(*first, false); + } + + return *this; + } + + /** + * @brief Assigns a writable resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &rw(const id_type res) { + emplace(res, true); + return *this; + } + + /** + * @brief Assigns a range of writable resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + rw(It first, It last) { + for(; first != last; ++first) { + emplace(*first, true); + } + + return *this; + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency matrix of the task graph. + */ + [[nodiscard]] graph_type graph() const { + graph_type matrix{vertices.size(), get_allocator()}; + + setup_graph(matrix); + transitive_closure(matrix); + transitive_reduction(matrix); + + return matrix; + } + +private: + compressed_pair index; + task_container_type vertices; + deps_container_type deps; + size_type sync_on{}; +}; + +} // namespace entt + +#endif + +// #include "locator/locator.hpp" +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/** + * @brief Service locator, nothing more. + * + * A service locator is used to do what it promises: locate services.
+ * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template +class locator final { + class service_handle { + friend class locator; + std::shared_ptr value{}; + }; + +public: + /*! @brief Service type. */ + using type = Service; + /*! @brief Service node type. */ + using node_type = service_handle; + + /*! @brief Default constructor, deleted on purpose. */ + locator() = delete; + + /*! @brief Default copy constructor, deleted on purpose. */ + locator(const locator &) = delete; + + /*! @brief Default destructor, deleted on purpose. */ + ~locator() = delete; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This locator. + */ + locator &operator=(const locator &) = delete; + + /** + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. + */ + [[nodiscard]] static bool has_value() noexcept { + return (service != nullptr); + } + + /** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ + [[nodiscard]] static Service &value() noexcept { + ENTT_ASSERT(has_value(), "Service not available"); + return *service; + } + + /** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Type Fallback service type. + * @param args Parameters to use to construct the fallback service. + * @return A reference to a valid service. + */ + template + [[nodiscard]] static Service &value_or(Args &&...args) { + return service ? *service : emplace(std::forward(args)...); + } + + /** + * @brief Sets or replaces a service. + * @tparam Type Service type. + * @tparam Args Types of arguments to use to construct the service. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template + static Service &emplace(Args &&...args) { + service = std::make_shared(std::forward(args)...); + return *service; + } + + /** + * @brief Sets or replaces a service using a given allocator. + * @tparam Type Service type. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the service. + * @param alloc The allocator to use. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template + static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared(alloc, std::forward(args)...); + return *service; + } + + /** + * @brief Returns a handle to the underlying service. + * @return A handle to the underlying service. + */ + static node_type handle() noexcept { + node_type node{}; + node.value = service; + return node; + } + + /** + * @brief Resets or replaces a service. + * @param other Optional handle with which to replace the service. + */ + static void reset(const node_type &other = {}) noexcept { + service = other.value; + } + + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr{elem, std::move(deleter)}; + } + +private: + // std::shared_ptr because of its type erased allocator which is useful here + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + inline static std::shared_ptr service{}; +}; + +} // namespace entt + +#endif + +// #include "meta/adl_pointer.hpp" +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template +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); + } +}; + +} // namespace entt + +#endif + +// #include "meta/container.hpp" +// IWYU pragma: always_keep + +#ifndef ENTT_META_CONTAINER_HPP +#define ENTT_META_CONTAINER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator> +class dense_set; + +template +class basic_table; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Element types. + */ +template +using table = basic_table...>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits::max)(); + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + const dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + const dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept = default; + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return (offset = it[static_cast(offset)].next), *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + const dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + const auto idx = static_cast(offset); + return {it[idx].element.first, it[idx].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_map_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_map() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses a given element with bounds checking. + * @tparam Other Type of the key of an element to find. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/bit.hpp" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_set_placeholder_position = (std::numeric_limits::max)(); + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + const dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + const dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept = default; + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[static_cast(offset)].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + const dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[static_cast(offset)].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_set_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_set_placeholder_position; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) { + if(packed.second()(packed.first()[offset].second, value)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].first) { + if(packed.second()(packed.first()[offset].second, value)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[value_to_bucket(packed.first().back().second)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_set() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array 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 noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = &sparse.first()[value_to_bucket(value)]; *curr != placeholder_position; curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "context.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +#include +// #include "../container/dense_map.hpp" + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_META_FWD_HPP +#define ENTT_META_FWD_HPP + +#include +#include + +namespace entt { + +class meta_ctx; + +class meta_sequence_container; + +class meta_associative_container; + +class meta_any; + +class meta_handle; + +struct meta_custom; + +class meta_data; + +class meta_func; + +class meta_type; + +template +class meta_factory; + +/*! @brief Used to identicate that a sequence container has not a fixed size. */ +inline constexpr std::size_t meta_dynamic_extent = (std::numeric_limits::max)(); + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_type_node; + +struct meta_context { + dense_map, identity> value; + + [[nodiscard]] inline static meta_context &from(meta_ctx &); + [[nodiscard]] inline static const meta_context &from(const meta_ctx &); +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Disambiguation tag for constructors and the like. */ +class meta_ctx_arg_t final {}; + +/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ +inline constexpr meta_ctx_arg_t meta_ctx_arg{}; + +/*! @brief Opaque meta context type. */ +class meta_ctx: private internal::meta_context { + // attorney idiom like model to access the base class + friend struct internal::meta_context; +}; + +/*! @cond TURN_OFF_DOXYGEN */ +[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { + return ctx; +} + +[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { + return ctx; +} +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#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 +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct fnv_1a_params; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 2166136261; + static constexpr auto prime = 16777619; +}; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 14695981039346656037ull; + static constexpr auto prime = 1099511628211ull; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr{}; + hash_type hash{fnv_1a_params<>::offset}; + size_type length{}; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using params = internal::fnv_1a_params<>; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const typename base_type::value_type *str) noexcept + : repr{str} {} + + const typename base_type::value_type *repr; + }; + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + [[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : basic_hashed_string{nullptr, 0u} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; base_type::length < len; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + for(; str[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + } + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{wrapper.repr} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; wrapper.repr[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(wrapper.repr[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Returns the size of a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] explicit constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_string operator""_hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_wstring operator""_hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr const char *pretty_function() noexcept { +#if defined ENTT_PRETTY_FUNCTION + return static_cast(ENTT_PRETTY_FUNCTION); +#else + return ""; +#endif +} + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + const std::string_view full_name{pretty_function()}; + auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static const type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_request : std::uint8_t { + info, + transfer, + assign, + compare, + copy, + move +}; + +template +struct basic_any_storage { + static constexpr bool has_buffer = true; + union { + const void *instance{}; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + alignas(Align) std::byte buffer[Len]; + }; +}; + +template +struct basic_any_storage<0u, Align> { + static constexpr bool has_buffer = false; + const void *instance{}; +}; + +template +// NOLINTNEXTLINE(bugprone-sizeof-expression) +struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v> {}; + +template +struct in_situ: std::false_type {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any: private internal::basic_any_storage { + using request = internal::any_request; + using base_type = internal::basic_any_storage; + using vtable_type = const void *(const request, const basic_any &, const void *); + using deleter_type = void(const basic_any &); + + template + static constexpr bool in_situ_v = internal::in_situ::value; + + template + static const void *basic_vtable(const request req, const basic_any &value, const void *other) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + + switch(const auto *elem = static_cast(value.data()); req) { + case request::info: + return &type_id(); + case request::transfer: + if constexpr(std::is_move_assignable_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + *const_cast(elem) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case request::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(elem) = *static_cast(other); + return other; + } + break; + case request::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return (*elem == *static_cast(other)) ? other : nullptr; + } else { + return (elem == other) ? other : nullptr; + } + case request::copy: + if constexpr(std::is_copy_constructible_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + static_cast(const_cast(other))->initialize(*elem); + } + break; + case request::move: + ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy"); + if constexpr(in_situ_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion) + return ::new(&static_cast(const_cast(other))->buffer) Type{std::move(*const_cast(elem))}; + } + } + + return nullptr; + } + + template + static void basic_deleter(const basic_any &value) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !std::is_trivially_destructible_v), "Unexpected policy"); + + const auto *elem = static_cast(value.data()); + + if constexpr(in_situ_v) { + (value.mode == any_policy::embedded) ? elem->~Type() : (delete elem); + } else if constexpr(std::is_array_v) { + delete[] elem; + } else { + delete elem; + } + } + + template + void initialize([[maybe_unused]] Args &&...args) { + using plain_type = std::remove_const_t>; + + vtable = basic_vtable; + underlying_type = type_hash::value(); + + if constexpr(std::is_void_v) { + deleter = nullptr; + mode = any_policy::empty; + this->instance = nullptr; + } else if constexpr(std::is_lvalue_reference_v) { + deleter = nullptr; + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) + this->instance = (std::addressof(args), ...); + } else if constexpr(in_situ_v) { + if constexpr(std::is_trivially_destructible_v) { + deleter = nullptr; + } else { + deleter = &basic_deleter; + } + + mode = any_policy::embedded; + + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + ::new(&this->buffer) plain_type{std::forward(args)...}; + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + ::new(&this->buffer) plain_type(std::forward(args)...); + } + } else { + deleter = &basic_deleter; + mode = any_policy::dynamic; + + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + this->instance = new plain_type{std::forward(args)...}; + } else if constexpr(std::is_array_v) { + static_assert(sizeof...(Args) == 0u, "Invalid arguments"); + this->instance = new plain_type[std::extent_v](); + } else { + this->instance = new plain_type(std::forward(args)...); + } + } + } + + void invoke_deleter_if_exists() { + if(deleter != nullptr) { + deleter(*this); + } + } + +public: + /*! @brief Size of the internal buffer. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : base_type{} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value A pointer to an object to take ownership of. + */ + template + explicit basic_any(std::in_place_t, Type *value) + : base_type{} { + static_assert(!std::is_const_v && !std::is_void_v, "Non-const non-void pointer required"); + + if(value == nullptr) { + initialize(); + } else { + initialize(*value); + deleter = &basic_deleter; + mode = any_policy::dynamic; + } + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, 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{} { + other.vtable(request::copy, other, this); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : base_type{}, + vtable{other.vtable}, + deleter{other.deleter}, + underlying_type{other.underlying_type}, + mode{other.mode} { + if(other.mode == any_policy::embedded) { + other.vtable(request::move, other, this); + } else if(other.mode != any_policy::empty) { + this->instance = std::exchange(other.instance, nullptr); + } + } + + /*! @brief Frees the internal buffer, whatever it means. */ + ~basic_any() { + invoke_deleter_if_exists(); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + if(this != &other) { + invoke_deleter_if_exists(); + + if(other) { + other.vtable(request::copy, other, this); + } else { + initialize(); + } + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + if(this != &other) { + invoke_deleter_if_exists(); + + if(other.mode == any_policy::embedded) { + other.vtable(request::move, other, this); + } else if(other.mode != any_policy::empty) { + this->instance = std::exchange(other.instance, nullptr); + } + + vtable = other.vtable; + deleter = other.deleter; + underlying_type = other.underlying_type; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template, basic_any>>> + basic_any &operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] bool has_value() const noexcept { + return (mode != any_policy::empty); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @param req Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + [[nodiscard]] bool has_value(const type_info &req) const noexcept { + return (underlying_type == req.hash()); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @tparam Type Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + template + [[nodiscard]] bool has_value() const noexcept { + static_assert(std::is_same_v, Type>, "Invalid type"); + return (underlying_type == type_hash::value()); + } + + /** + * @brief Returns the object type info if any, `type_id()` otherwise. + * @return The object type info if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *static_cast(vtable(request::info, *this, nullptr)); + } + + /*! @copydoc info */ + [[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept { + return info(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + if constexpr(base_type::has_buffer) { + return (mode == any_policy::embedded) ? &this->buffer : this->instance; + } else { + return this->instance; + } + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return has_value(req) ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @tparam Type Expected type. + * @return An opaque pointer the contained instance, if any. + */ + template + [[nodiscard]] const Type *data() const noexcept { + return has_value>() ? static_cast(data()) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @tparam Type Expected type. + * @return An opaque pointer the contained instance, if any. + */ + template + [[nodiscard]] Type *data() noexcept { + if constexpr(std::is_const_v) { + return std::as_const(*this).template data>(); + } else { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).template data>()); + } + } + + /** + * @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) { + invoke_deleter_if_exists(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) { + return (vtable(request::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + bool assign(basic_any &&other) { + if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) { + return (other.mode == any_policy::cref) ? (vtable(request::assign, *this, std::as_const(other).data()) != nullptr) : (vtable(request::transfer, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @brief Destroys contained object */ + void reset() { + invoke_deleter_if_exists(); + initialize(); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return has_value(); + } + + /** + * @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 basic_any &other) const noexcept { + if(other && (underlying_type == other.underlying_type)) { + return (vtable(request::compare, *this, other.data()) != nullptr); + } + + return (!*this && !other); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + basic_any other = std::as_const(*this).as_ref(); + other.mode = (mode == any_policy::cref ? any_policy::cref : any_policy::ref); + return other; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + basic_any other{}; + other.instance = data(); + other.vtable = vtable; + other.underlying_type = underlying_type; + other.mode = any_policy::cref; + return other; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::dynamic || mode == any_policy::embedded); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + vtable_type *vtable{}; + deleter_type *deleter{}; + id_type underlying_type{}; + any_policy mode{}; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] std::remove_const_t any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] std::remove_const_t any_cast(basic_any &data) 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, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) +[[nodiscard]] std::remove_const_t any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } + + return any_cast(data); + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + return data->template data>(); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + return data->template data(); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr const char *pretty_function() noexcept { +#if defined ENTT_PRETTY_FUNCTION + return static_cast(ENTT_PRETTY_FUNCTION); +#else + return ""; +#endif +} + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + const std::string_view full_name{pretty_function()}; + auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static const type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "../locator/locator.hpp" +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/** + * @brief Service locator, nothing more. + * + * A service locator is used to do what it promises: locate services.
+ * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template +class locator final { + class service_handle { + friend class locator; + std::shared_ptr value{}; + }; + +public: + /*! @brief Service type. */ + using type = Service; + /*! @brief Service node type. */ + using node_type = service_handle; + + /*! @brief Default constructor, deleted on purpose. */ + locator() = delete; + + /*! @brief Default copy constructor, deleted on purpose. */ + locator(const locator &) = delete; + + /*! @brief Default destructor, deleted on purpose. */ + ~locator() = delete; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This locator. + */ + locator &operator=(const locator &) = delete; + + /** + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. + */ + [[nodiscard]] static bool has_value() noexcept { + return (service != nullptr); + } + + /** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ + [[nodiscard]] static Service &value() noexcept { + ENTT_ASSERT(has_value(), "Service not available"); + return *service; + } + + /** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Type Fallback service type. + * @param args Parameters to use to construct the fallback service. + * @return A reference to a valid service. + */ + template + [[nodiscard]] static Service &value_or(Args &&...args) { + return service ? *service : emplace(std::forward(args)...); + } + + /** + * @brief Sets or replaces a service. + * @tparam Type Service type. + * @tparam Args Types of arguments to use to construct the service. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template + static Service &emplace(Args &&...args) { + service = std::make_shared(std::forward(args)...); + return *service; + } + + /** + * @brief Sets or replaces a service using a given allocator. + * @tparam Type Service type. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the service. + * @param alloc The allocator to use. + * @param args Parameters to use to construct the service. + * @return A reference to a valid service. + */ + template + static Service &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared(alloc, std::forward(args)...); + return *service; + } + + /** + * @brief Returns a handle to the underlying service. + * @return A handle to the underlying service. + */ + static node_type handle() noexcept { + node_type node{}; + node.value = service; + return node; + } + + /** + * @brief Resets or replaces a service. + * @param other Optional handle with which to replace the service. + */ + static void reset(const node_type &other = {}) noexcept { + service = other.value; + } + + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr{elem, std::move(deleter)}; + } + +private: + // std::shared_ptr because of its type erased allocator which is useful here + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + inline static std::shared_ptr service{}; +}; + +} // namespace entt + +#endif + +// #include "adl_pointer.hpp" +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template +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); + } +}; + +} // namespace entt + +#endif + +// #include "context.hpp" + +// #include "fwd.hpp" + +// #include "node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "../core/enum.hpp" +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include + +namespace entt { + +/** + * @brief Enable bitmask support for enum classes. + * @tparam Type The enum type for which to enable bitmask support. + */ +template +struct enum_as_bitmask: std::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template +struct enum_as_bitmask>: std::is_enum {}; + +/** + * @brief Helper variable template. + * @tparam Type The enum class type for which to enable bitmask support. + */ +template +inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; + +} // namespace entt + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param lhs The first value to use. + * @param rhs The second value to use. + * @return The result of invoking the operator on the underlying types of the + * two values provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator|(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator&(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator^(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param value The value to use. + * @return The result of invoking the operator on the underlying types of the + * value provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator~(const Type value) noexcept { + return static_cast(~static_cast>(value)); +} + +/*! @copydoc operator~ */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> +operator!(const Type value) noexcept { + return !static_cast>(value); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator|=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator&=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator^=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs ^ rhs)); +} + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "context.hpp" + +// #include "type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include +#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 given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + */ +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; + +} // namespace entt + +#endif + + +namespace entt { + +class meta_any; +class meta_type; +class meta_handle; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class meta_traits : std::uint32_t { + is_none = 0x0000, + is_const = 0x0001, + is_static = 0x0002, + is_arithmetic = 0x0004, + is_integral = 0x0008, + is_signed = 0x0010, + is_array = 0x0020, + is_enum = 0x0040, + is_class = 0x0080, + is_pointer = 0x0100, + is_pointer_like = 0x0200, + is_sequence_container = 0x0400, + is_associative_container = 0x0800, + _user_defined_traits = 0xFFFF, + _entt_enum_as_bitmask = 0xFFFF +}; + +template +[[nodiscard]] auto meta_to_user_traits(const meta_traits traits) noexcept { + static_assert(std::is_enum_v, "Invalid enum type"); + constexpr auto shift = popcount(static_cast>(meta_traits::_user_defined_traits)); + return Type{static_cast>(static_cast>(traits) >> shift)}; +} + +template +[[nodiscard]] auto user_to_meta_traits(const Type value) noexcept { + static_assert(std::is_enum_v, "Invalid enum type"); + constexpr auto shift = popcount(static_cast>(meta_traits::_user_defined_traits)); + const auto traits = static_cast>(static_cast>(value)); + ENTT_ASSERT(traits < ((~static_cast>(meta_traits::_user_defined_traits)) >> shift), "Invalid traits"); + return meta_traits{traits << shift}; +} + +struct meta_type_node; + +struct meta_custom_node { + id_type type{}; + std::shared_ptr value{}; +}; + +struct meta_base_node { + id_type type{}; + const meta_type_node &(*resolve)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; +}; + +struct meta_conv_node { + id_type type{}; + meta_any (*conv)(const meta_ctx &, const void *){}; +}; + +struct meta_ctor_node { + using size_type = std::size_t; + + id_type id{}; + size_type arity{0u}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_any *const){}; +}; + +struct meta_data_node { + using size_type = std::size_t; + + id_type id{}; + const char *name{}; + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + const meta_type_node &(*type)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + bool (*set)(meta_handle, meta_any){}; + meta_any (*get)(meta_handle){}; + meta_custom_node custom{}; +}; + +struct meta_func_node { + using size_type = std::size_t; + + id_type id{}; + const char *name{}; + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + const meta_type_node &(*ret)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(meta_handle, meta_any *const){}; + std::unique_ptr next; + meta_custom_node custom{}; +}; + +struct meta_template_node { + using size_type = std::size_t; + + size_type arity{0u}; + const meta_type_node &(*resolve)(const meta_context &) noexcept {}; + const meta_type_node &(*arg)(const meta_context &, const size_type) noexcept {}; +}; + +struct meta_type_descriptor { + std::vector ctor{}; + std::vector base{}; + std::vector conv{}; + std::vector data{}; + std::vector func{}; +}; + +struct meta_type_node { + using size_type = std::size_t; + + const type_info *info{}; + id_type id{}; + const char *name{}; + meta_traits traits{meta_traits::is_none}; + size_type size_of{0u}; + const meta_type_node &(*remove_pointer)(const meta_context &) noexcept {}; + meta_any (*default_constructor)(const meta_ctx &){}; + double (*conversion_helper)(void *, const void *){}; + meta_any (*from_void)(const meta_ctx &, void *, const void *){}; + meta_template_node templ{}; + meta_custom_node custom{}; + std::unique_ptr details{}; +}; + +template +[[nodiscard]] auto *find_member(Type &from, const Value value) { + for(auto &&elem: from) { + if((elem.*Member) == value) { + return &elem; + } + } + + return static_cast(nullptr); +} + +[[nodiscard]] inline auto *find_overload(meta_func_node *curr, std::remove_pointer_t *const ref) { + while((curr != nullptr) && (curr->invoke != ref)) { curr = curr->next.get(); } + return curr; +} + +template +[[nodiscard]] auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id, bool recursive) { + using value_type = typename std::remove_reference_t*Member))>::value_type; + + if(node.details) { + if(auto *member = find_member<&value_type::id>((node.details.get()->*Member), id); member != nullptr) { + return member; + } + + if(recursive) { + for(auto &&curr: node.details->base) { + if(auto *elem = look_for(context, curr.resolve(context), id, recursive); elem) { + return elem; + } + } + } + } + + return static_cast(nullptr); +} + +template +const meta_type_node &resolve(const meta_context &) noexcept; + +template +[[nodiscard]] const meta_type_node &meta_arg_node(const meta_context &context, type_list, const std::size_t index) noexcept { + using resolve_type = const meta_type_node &(*)(const meta_context &) noexcept; + constexpr std::array list{&resolve>>...}; + ENTT_ASSERT(index < sizeof...(Args), "Out of bounds"); + return list[index](context); +} + +[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const id_type to, const void *instance) noexcept { + if(from.details) { + for(auto &&curr: from.details->base) { + if(const void *other = curr.cast(instance); curr.type == to) { + return other; + } else if(const void *elem = try_cast(context, curr.resolve(context), to, other); elem) { + return elem; + } + } + } + + return nullptr; +} + +template +auto setup_node_for() noexcept { + meta_type_node node{ + &type_id(), + type_id().hash(), + nullptr, + (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) + | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) + | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) + | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) + | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) + | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) + | (std::is_pointer_v ? meta_traits::is_pointer : meta_traits::is_none) + | (is_meta_pointer_like_v ? meta_traits::is_pointer_like : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_sequence_container : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_associative_container : meta_traits::is_none), + size_of_v, + &resolve>>}; + + if constexpr(std::is_default_constructible_v) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, std::in_place_type}; + }; + } + + if constexpr(std::is_arithmetic_v) { + node.conversion_helper = +[](void *lhs, const void *rhs) { + return lhs ? static_cast(*static_cast(lhs) = static_cast(*static_cast(rhs))) : static_cast(*static_cast(rhs)); + }; + } else if constexpr(std::is_enum_v) { + node.conversion_helper = +[](void *lhs, const void *rhs) { + return lhs ? static_cast(*static_cast(lhs) = static_cast(static_cast>(*static_cast(rhs)))) : static_cast(*static_cast(rhs)); + }; + } + + if constexpr(!std::is_void_v && !std::is_function_v) { + node.from_void = +[](const meta_ctx &ctx, void *elem, const void *celem) { + if(elem && celem) { // ownership construction request + return meta_any{ctx, std::in_place, static_cast *>(elem)}; + } + + if(elem) { // non-const reference construction request + return meta_any{ctx, std::in_place_type &>, *static_cast *>(elem)}; + } + + // const reference construction request + return meta_any{ctx, std::in_place_type &>, *static_cast *>(celem)}; + }; + } + + if constexpr(is_complete_v>) { + node.templ = meta_template_node{ + meta_template_traits::args_type::size, + &resolve::class_type>, + +[](const meta_context &area, const std::size_t index) noexcept -> decltype(auto) { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; + } + + return node; +} + +[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { + const auto it = context.value.find(info.hash()); + return (it != context.value.end()) ? it->second.get() : nullptr; +} + +template +[[nodiscard]] const meta_type_node &resolve(const meta_context &context) noexcept { + static_assert(std::is_same_v>>, "Invalid type"); + static const meta_type_node node = setup_node_for(); + const auto *elem = try_resolve(context, *node.info); + return (elem == nullptr) ? node : *elem; +} + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "context.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_base_node; + +template +struct meta_range_iterator final { + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr meta_range_iterator() noexcept + : it{}, + ctx{} {} + + constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept + : it{iter}, + ctx{&area} {} + + constexpr meta_range_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr meta_range_iterator operator++(int) noexcept { + const meta_range_iterator orig = *this; + return ++(*this), orig; + } + + constexpr meta_range_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr meta_range_iterator operator--(int) noexcept { + const meta_range_iterator orig = *this; + return operator--(), orig; + } + + constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr meta_range_iterator operator+(const difference_type value) const noexcept { + meta_range_iterator copy = *this; + return (copy += value); + } + + constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr meta_range_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + if constexpr(std::is_same_v) { + return {it[value].first, Type{*ctx, *it[value].second}}; + } else if constexpr(std::is_same_v::value_type, meta_base_node>) { + return {it[value].type, Type{*ctx, it[value]}}; + } else { + return {it[value].id, Type{*ctx, it[value]}}; + } + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; + +private: + It it; + const meta_ctx *ctx; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam It Type of forward iterator. + */ +template +using meta_range = iterable_adaptor>; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_sequence_container() = default; + + /** + * @brief Context aware constructor. + * @tparam Type Type of container to wrap. + * @param area The context from which to search for meta types. + * @param instance The container to wrap. + */ + template + meta_sequence_container(const meta_ctx &area, Type &instance) noexcept + : ctx{&area}, + data{&instance}, + value_type_node{&internal::resolve}, + const_reference_node{&internal::resolve>>}, + size_fn{meta_sequence_container_traits>::size}, + clear_fn{meta_sequence_container_traits>::clear}, + reserve_fn{meta_sequence_container_traits>::reserve}, + resize_fn{meta_sequence_container_traits>::resize}, + begin_end_fn{meta_sequence_container_traits>::iter}, + insert_fn{meta_sequence_container_traits>::insert}, + erase_fn{meta_sequence_container_traits>::erase}, + const_only{std::is_const_v} {} + + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool resize(size_type); + inline bool clear(); + inline bool reserve(size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline iterator insert(const iterator &, meta_any); + inline iterator erase(const iterator &); + [[nodiscard]] inline meta_any operator[](size_type); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + const void *data{}; + const internal::meta_type_node &(*value_type_node)(const internal::meta_context &){}; + const internal::meta_type_node &(*const_reference_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + bool (*resize_fn)(void *, const size_type){}; + iterator (*begin_end_fn)(const meta_ctx &, void *, const void *, const bool){}; + iterator (*insert_fn)(const meta_ctx &, void *, const void *, const void *, const iterator &){}; + iterator (*erase_fn)(const meta_ctx &, void *, const iterator &){}; + bool const_only{}; +}; + +/*! @brief Proxy object for associative containers. */ +class meta_associative_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_associative_container() = default; + + /** + * @brief Context aware constructor. + * @tparam Type Type of container to wrap. + * @param area The context from which to search for meta types. + * @param instance The container to wrap. + */ + template + meta_associative_container(const meta_ctx &area, Type &instance) noexcept + : ctx{&area}, + data{&instance}, + key_type_node{&internal::resolve}, + value_type_node{&internal::resolve}, + size_fn{&meta_associative_container_traits>::size}, + clear_fn{&meta_associative_container_traits>::clear}, + reserve_fn{&meta_associative_container_traits>::reserve}, + begin_end_fn{&meta_associative_container_traits>::iter}, + insert_fn{&meta_associative_container_traits>::insert}, + erase_fn{&meta_associative_container_traits>::erase}, + find_fn{&meta_associative_container_traits>::find}, + const_only{std::is_const_v} { + if constexpr(!meta_associative_container_traits>::key_only) { + mapped_type_node = &internal::resolve; + } + } + + [[nodiscard]] inline meta_type key_type() const noexcept; + [[nodiscard]] inline meta_type mapped_type() const noexcept; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool clear(); + inline bool reserve(size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline size_type erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + const void *data{}; + const internal::meta_type_node &(*key_type_node)(const internal::meta_context &){}; + const internal::meta_type_node &(*mapped_type_node)(const internal::meta_context &){}; + const internal::meta_type_node &(*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + iterator (*begin_end_fn)(const meta_ctx &, void *, const void *, const bool){}; + bool (*insert_fn)(void *, const void *, const void *){}; + size_type (*erase_fn)(void *, const void *){}; + iterator (*find_fn)(const meta_ctx &, void *, const void *, const void *){}; + bool const_only{}; +}; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { + using vtable_type = void(const internal::meta_traits, const meta_any &, const void *); + + template + static void basic_vtable(const internal::meta_traits req, const meta_any &value, [[maybe_unused]] const void *other) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + + if(req == internal::meta_traits::is_none) { + value.node = &internal::resolve(internal::meta_context::from(*value.ctx)); + } + + if constexpr(is_meta_pointer_like_v) { + if(req == internal::meta_traits::is_pointer_like) { + if constexpr(std::is_function_v::element_type>) { + const_cast(value).emplace(*static_cast(other)); + } else if constexpr(!std::is_void_v::element_type>>) { + using in_place_type = decltype(adl_meta_pointer_like::dereference(std::declval())); + + if constexpr(std::is_constructible_v) { + if(const auto &pointer_like = *static_cast(other); pointer_like) { + const_cast(value).emplace(adl_meta_pointer_like::dereference(pointer_like)); + } + } else { + const_cast(value).emplace(adl_meta_pointer_like::dereference(*static_cast(other))); + } + } + } + } + + if constexpr(is_complete_v> || is_complete_v>) { + if(constexpr auto flag = (is_complete_v> ? internal::meta_traits::is_sequence_container : internal::meta_traits::is_associative_container); !!(req & flag)) { + using container_type = std::conditional_t>, meta_sequence_container, meta_associative_container>; + + if(!!(req & internal::meta_traits::is_const) || (value.storage.policy() == any_policy::cref)) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + *static_cast(const_cast(other)) = container_type{*value.ctx, any_cast(value.storage)}; + } else { + // NOLINTNEXTLINE(bugprone-casting-through-void) + *static_cast(const_cast(other)) = container_type{*value.ctx, any_cast(const_cast(value).storage)}; + } + } + } + } + + [[nodiscard]] const auto &fetch_node() const { + if(node == nullptr) { + ENTT_ASSERT(*this, "Invalid vtable function"); + vtable(internal::meta_traits::is_none, *this, nullptr); + } + + ENTT_ASSERT(node != nullptr, "Invalid pointer to node"); + return *node; + } + + meta_any(const meta_any &other, any elem) + : storage{std::move(elem)}, + ctx{other.ctx}, + node{other.node}, + vtable{other.vtable} {} + +public: + /*! Default constructor. */ + meta_any() = default; + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_any(meta_ctx_arg_t, const meta_ctx &area) + : ctx{&area} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(std::in_place_type_t, Args &&...args) + : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param area The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + ctx{&area}, + vtable{&basic_vtable>>} {} + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value A pointer to an object to take ownership of. + */ + template + explicit meta_any(std::in_place_t, Type *value) + : meta_any{locator::value_or(), std::in_place, value} {} + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value A pointer to an object to take ownership of. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_t, Type *value) + : storage{std::in_place, value}, + ctx{&area}, + vtable{storage ? &basic_vtable : nullptr} { + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(Type &&value) + : meta_any{locator::value_or(), std::forward(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(const meta_ctx &area, Type &&value) + : meta_any{area, std::in_place_type>, std::forward(value)} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_any(const meta_ctx &area, const meta_any &other) + : storage{other.storage}, + ctx{&area}, + node{(ctx == other.ctx) ? other.node : nullptr}, + vtable{other.vtable} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_any(const meta_ctx &area, meta_any &&other) + : storage{std::move(other.storage)}, + ctx{&area}, + node{(ctx == other.ctx) ? std::exchange(other.node, nullptr) : nullptr}, + vtable{std::exchange(other.vtable, nullptr)} {} + + /** + * @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) noexcept + : storage{std::move(other.storage)}, + ctx{other.ctx}, + node{std::exchange(other.node, nullptr)}, + vtable{std::exchange(other.vtable, nullptr)} {} + + /*! @brief Default destructor. */ + ~meta_any() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ + meta_any &operator=(const meta_any &other) { + if(this != &other) { + storage = other.storage; + ctx = other.ctx; + node = other.node; + vtable = other.vtable; + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any &operator=(meta_any &&other) noexcept { + storage = std::move(other.storage); + ctx = other.ctx; + node = std::exchange(other.node, nullptr); + vtable = std::exchange(other.vtable, 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>>> + meta_any &operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /*! @copydoc any::info */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(id_type id, Args &&...args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(id_type id, Args &&...args); + + /** + * @brief Sets the value of a given variable. + * @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(id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(id_type id) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(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 { + const auto *elem = any_cast(&storage); + return ((elem != nullptr) || !*this) ? elem : static_cast(internal::try_cast(internal::meta_context::from(*ctx), fetch_node(), type_hash>::value(), storage.data())); + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type *try_cast() { + return ((storage.policy() == any_policy::cref) && !std::is_const_v) ? nullptr : const_cast(std::as_const(*this).try_cast>()); + } + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ + template + [[nodiscard]] std::remove_const_t cast() const { + auto *const instance = try_cast>(); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); + } + + /*! @copydoc cast */ + template + [[nodiscard]] std::remove_const_t 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, "Invalid instance"); + return static_cast(*instance); + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return A valid meta object if convertible, an invalid one otherwise. + */ + [[nodiscard]] meta_any allow_cast(const meta_type &type) const; + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return True if convertible, false otherwise. + */ + [[nodiscard]] bool allow_cast(const meta_type &type); + + /** + * @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 object if convertible, an invalid one otherwise. + */ + template + [[nodiscard]] meta_any allow_cast() const { + if constexpr(!std::is_reference_v || std::is_const_v>) { + if(storage.has_value>>()) { + return as_ref(); + } else if(*this) { + if constexpr(std::is_arithmetic_v>> || std::is_enum_v>>) { + if(const auto &from = fetch_node(); from.conversion_helper) { + return meta_any{*ctx, static_cast(from.conversion_helper(nullptr, storage.data()))}; + } + } + + if(const auto &from = fetch_node(); from.details != nullptr) { + if(const auto *elem = internal::find_member<&internal::meta_conv_node::type>(from.details->conv, entt::type_hash>>::value()); elem != nullptr) { + return elem->conv(*ctx, storage.data()); + } + + for(auto &&curr: from.details->base) { + if(auto other = curr.resolve(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.type == entt::type_hash>>::value()) { + return other; + } else if(auto from_base = std::as_const(other).template allow_cast(); from_base) { + return from_base; + } + } + } + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @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 convertible, false otherwise. + */ + template + [[nodiscard]] bool allow_cast() { + if constexpr(std::is_reference_v && !std::is_const_v>) { + return allow_cast &>() && (storage.policy() != any_policy::cref); + } else { + if(storage.has_value>>()) { + return true; + } else if(auto other = std::as_const(*this).allow_cast>>(); other) { + if(other.storage.owner()) { + std::swap(*this, other); + } + + return true; + } + + return false; + } + } + + /*! @copydoc any::emplace */ + template + void emplace(Args &&...args) { + storage.emplace(std::forward(args)...); + auto *prev = std::exchange(vtable, &basic_vtable>>); + node = (prev == vtable) ? node : nullptr; + } + + /*! @copydoc any::assign */ + bool assign(const meta_any &other); + + /*! @copydoc any::assign */ + bool assign(meta_any &&other); + + /*! @copydoc any::reset */ + void reset() { + storage.reset(); + node = nullptr; + vtable = nullptr; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { + meta_sequence_container proxy{}; + if(*this) { vtable(internal::meta_traits::is_sequence_container, *this, &proxy); } + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { + meta_sequence_container proxy{}; + if(*this) { vtable(internal::meta_traits::is_sequence_container | internal::meta_traits::is_const, *this, &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() noexcept { + meta_associative_container proxy{}; + if(*this) { vtable(internal::meta_traits::is_associative_container, *this, &proxy); } + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const noexcept { + meta_associative_container proxy{}; + if(*this) { vtable(internal::meta_traits::is_associative_container | internal::meta_traits::is_const, *this, &proxy); } + return proxy; + } + + /** + * @brief Indirection operator for dereferencing opaque objects. + * @return A wrapper that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; + if(*this) { vtable(internal::meta_traits::is_pointer_like, ret, storage.data()); } + return ret; + } + + /*! @copydoc any::operator bool */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(vtable == nullptr); + } + + /*! @copydoc any::operator== */ + [[nodiscard]] bool operator==(const meta_any &other) const noexcept { + return (ctx == other.ctx) && (!*this == !other) && (storage == other.storage); + } + + /*! @copydoc any::operator!= */ + [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { + return !(*this == other); + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() noexcept { + return meta_any{*this, storage.as_ref()}; + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() const noexcept { + return meta_any{*this, storage.as_ref()}; + } + + /** + * @brief Returns the underlying storage. + * @return The underlyig storage. + */ + [[nodiscard]] const any &base() const noexcept { + return storage; + } + + /** + * @brief Returns the underlying meta context. + * @return The underlying meta context. + */ + [[nodiscard]] const meta_ctx &context() const noexcept { + return *ctx; + } + +private: + any storage{}; + const meta_ctx *ctx{&locator::value_or()}; + mutable const internal::meta_type_node *node{}; + vtable_type *vtable{}; +}; + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @param ctx The context from which to search for meta types. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { + return meta_any{ctx, std::in_place_type, std::forward(value)}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(Type &&value) { + return forward_as_meta(locator::value_or(), std::forward(value)); +} + +/*! @brief Opaque pointers to instances of any type. */ +class meta_handle { + template, meta_any>>> + meta_handle(int, Type &value, Args &&...args) + : any{std::forward(args)..., value.as_ref()} {} + + template + meta_handle(char, Type &value, Args &&...args) + : any{std::forward(args)..., std::in_place_type, value} {} + +public: + /*! Default constructor. */ + 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 ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(const meta_ctx &ctx, Type &value) + : meta_handle{0, value, ctx} {} + + /** + * @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) + : meta_handle{0, value} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_handle(const meta_ctx &area, meta_handle &&other) + : any{area, std::move(other.any)} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + meta_handle(const meta_handle &) = delete; + + /*! @brief Default move constructor. */ + meta_handle(meta_handle &&) = default; + + /*! @brief Default destructor. */ + ~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 Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(any); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any *operator->() { + return &any; + } + + /*! @copydoc operator-> */ + [[deprecated("do not use const handles")]] [[nodiscard]] const meta_any *operator->() const { + return &any; + } + +private: + meta_any any{}; +}; + +/*! @brief Opaque wrapper for user defined data of any type. */ +struct meta_custom { + /*! @brief Default constructor. */ + meta_custom() noexcept = default; + + /** + * @brief Basic constructor for meta objects. + * @param curr The underlying node with which to construct the instance. + */ + meta_custom(const internal::meta_custom_node &curr) noexcept + : node{&curr} {} + + /** + * @brief Generic conversion operator. + * @tparam Type Type to which conversion is requested. + */ + template + [[nodiscard]] operator Type *() const noexcept { + return ((node != nullptr) && (type_hash>::value() == node->type)) ? static_cast(node->value.get()) : nullptr; + } + + /** + * @brief Generic conversion operator. + * @tparam Type Type to which conversion is requested. + */ + template + [[nodiscard]] operator Type &() const noexcept { + ENTT_ASSERT(static_cast(*this) != nullptr, "Invalid type"); + return *static_cast(node->value.get()); + } + +private: + const internal::meta_custom_node *node{}; +}; + +/*! @brief Opaque wrapper for data members. */ +class meta_data { + [[nodiscard]] auto &node_or_assert() const noexcept { + ENTT_ASSERT(node != nullptr, "Invalid pointer to node"); + return *node; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_data_node::size_type; + + /*! @brief Default constructor. */ + meta_data() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the name assigned to a data member, if any. + * @return The name assigned to the data member, if any. + */ + [[nodiscard]] const char *name() const noexcept { + return node_or_assert().name; + } + + /** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ + [[nodiscard]] size_type arity() const noexcept { + return node_or_assert().arity; + } + + /** + * @brief Indicates whether a data member is constant or not. + * @return True if the data member is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return !!(node_or_assert().traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a data member is static or not. + * @return True if the data member is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return !!(node_or_assert().traits & internal::meta_traits::is_static); + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Sets the value of a given variable. + * @tparam Instance Type of instance to operate on. + * @tparam Type Type of value to assign. + * @param instance An instance that fits the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + bool set(Instance &&instance, Type &&value) const { + return node_or_assert().set(meta_handle{*ctx, std::forward(instance)}, meta_any{*ctx, std::forward(value)}); + } + + /** + * @brief Gets the value of a given variable. + * @tparam Instance Type of instance to operate on. + * @param instance An instance that fits the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + template + [[nodiscard]] meta_any get(Instance &&instance) const { + return node_or_assert().get(meta_handle{*ctx, std::forward(instance)}); + } + + /** + * @brief Returns the type accepted by the i-th setter. + * @param index Index of the setter of which to return the accepted type. + * @return The type accepted by the i-th setter. + */ + [[nodiscard]] inline meta_type arg(size_type index) const noexcept; + + /** + * @brief Returns all meta traits for a given meta object. + * @tparam Type The type to convert the meta traits to. + * @return The registered meta traits, if any. + */ + template + [[nodiscard]] Type traits() const noexcept { + return internal::meta_to_user_traits(node_or_assert().traits); + } + + /** + * @brief Returns user defined data for a given meta object. + * @return User defined arbitrary data. + */ + [[nodiscard]] meta_custom custom() const noexcept { + return {node_or_assert().custom}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const 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_data &other) const noexcept { + return (ctx == other.ctx) && (node == other.node); + } + +private: + const internal::meta_data_node *node{}; + const meta_ctx *ctx{&locator::value_or()}; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for member functions. */ +class meta_func { + [[nodiscard]] auto &node_or_assert() const noexcept { + ENTT_ASSERT(node != nullptr, "Invalid pointer to node"); + return *node; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_func_node::size_type; + + /*! @brief Default constructor. */ + meta_func() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the name assigned to a member function, if any. + * @return The name assigned to the member function, if any. + */ + [[nodiscard]] const char *name() const noexcept { + return node_or_assert().name; + } + + /** + * @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 noexcept { + return node_or_assert().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 noexcept { + return !!(node_or_assert().traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a member function is static or not. + * @return True if the member function is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return !!(node_or_assert().traits & internal::meta_traits::is_static); + } + + /** + * @brief Returns the return type of a member function. + * @return The return type of the member function. + */ + [[nodiscard]] inline meta_type ret() const 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 noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * @tparam Instance Type of instance to operate on. + * @param instance An instance that fits the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(Instance &&instance, meta_any *const args, const size_type sz) const { + return (sz == arity()) ? node_or_assert().invoke(meta_handle{*ctx, std::forward(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @tparam Instance Type of instance to operate on. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An instance that fits the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(Instance &&instance, Args &&...args) const { + return invoke(std::forward(instance), std::array{meta_any{*ctx, std::forward(args)}...}.data(), sizeof...(Args)); + } + + /*! @copydoc meta_data::traits */ + template + [[nodiscard]] Type traits() const noexcept { + return internal::meta_to_user_traits(node_or_assert().traits); + } + + /*! @copydoc meta_data::custom */ + [[nodiscard]] meta_custom custom() const noexcept { + return {node_or_assert().custom}; + } + + /** + * @brief Returns the next overload of a given function, if any. + * @return The next overload of the given function, if any. + */ + [[nodiscard]] meta_func next() const { + return (node_or_assert().next != nullptr) ? meta_func{*ctx, *node_or_assert().next} : meta_func{}; + } + + /*! @copydoc meta_data::operator bool */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_data::operator== */ + [[nodiscard]] bool operator==(const meta_func &other) const noexcept { + return (ctx == other.ctx) && (node == other.node); + } + +private: + const internal::meta_func_node *node{}; + const meta_ctx *ctx{&locator::value_or()}; +}; + +/*! @copydoc operator!=(const meta_data &, const meta_data &) */ +[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for types. */ +class meta_type { + [[nodiscard]] const auto &fetch_node() const { + return (node == nullptr) ? internal::resolve(internal::meta_context::from(*ctx)) : *node; + } + + template + [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { + decltype(next()) candidate = nullptr; + size_type same{}; + bool ambiguous{}; + + for(auto curr = next(); curr; curr = next()) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(constness && !(curr->traits & internal::meta_traits::is_const)) { + continue; + } + } + + if(curr->arity == sz) { + size_type match{}; + size_type pos{}; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + for(; pos < sz && args[pos]; ++pos) { + const auto other = curr->arg(*ctx, pos); + const auto type = args[pos].type(); + + if(const auto &info = other.info(); info == type.info()) { + ++match; + } else if(!(type.fetch_node().conversion_helper && other.fetch_node().conversion_helper) && !(type.fetch_node().details && (internal::find_member<&internal::meta_base_node::type>(type.fetch_node().details->base, info.hash()) || internal::find_member<&internal::meta_conv_node::type>(type.fetch_node().details->conv, info.hash())))) { + break; + } + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + if(pos == sz) { + if(!candidate || match > same) { + candidate = curr; + same = match; + ambiguous = false; + } else if(match == same) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(!!(curr->traits & internal::meta_traits::is_const) != !!(candidate->traits & internal::meta_traits::is_const)) { + candidate = !!(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; + ambiguous = false; + continue; + } + } + + ambiguous = true; + } + } + } + } + + return ambiguous ? nullptr : candidate; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + + /*! @brief Default constructor. */ + meta_type() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept + : meta_type{area, curr.resolve(internal::meta_context::from(area))} {} + + /** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *fetch_node().info; + } + + /** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ + [[nodiscard]] id_type id() const noexcept { + return fetch_node().id; + } + + /** + * @brief Returns the name assigned to a type, if any. + * @return The name assigned to the type, if any. + */ + [[nodiscard]] const char *name() const noexcept { + return fetch_node().name; + } + + /** + * @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 noexcept { + return fetch_node().size_of; + } + + /** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ + [[nodiscard]] bool is_arithmetic() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_arithmetic); + } + + /** + * @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 noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_integral); + } + + /** + * @brief Checks whether a type refers to a signed type or not. + * @return True if the underlying type is a signed type, false otherwise. + */ + [[nodiscard]] bool is_signed() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_signed); + } + + /** + * @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 noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_array); + } + + /** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ + [[nodiscard]] bool is_enum() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_enum); + } + + /** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ + [[nodiscard]] bool is_class() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_class); + } + + /** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ + [[nodiscard]] bool is_pointer() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_pointer); + } + + /** + * @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 noexcept { + return meta_type{*ctx, fetch_node().remove_pointer(internal::meta_context::from(*ctx))}; + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is pointer-like, false otherwise. + */ + [[nodiscard]] bool is_pointer_like() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::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 noexcept { + return !!(fetch_node().traits & internal::meta_traits::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 noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_associative_container); + } + + /** + * @brief Checks whether a type refers to a template specialization or not. + * @return True if the type is a template specialization, false otherwise. + */ + [[nodiscard]] bool is_template_specialization() const noexcept { + return (fetch_node().templ.arity != 0u); + } + + /** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ + [[nodiscard]] size_type template_arity() const noexcept { + return fetch_node().templ.arity; + } + + /** + * @brief Returns a tag for the class template of the underlying type. + * @return The tag for the class template of the underlying type. + */ + [[nodiscard]] meta_type template_type() const noexcept { + return (fetch_node().templ.resolve != nullptr) ? meta_type{*ctx, fetch_node().templ.resolve(internal::meta_context::from(*ctx))} : 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]] meta_type template_arg(const size_type index) const noexcept { + return index < template_arity() ? meta_type{*ctx, fetch_node().templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; + } + + /** + * @brief Checks if a type supports direct casting to another type. + * @param other The meta type to test for. + * @return True if direct casting is allowed, false otherwise. + */ + [[nodiscard]] bool can_cast(const meta_type &other) const noexcept { + // casting this is UB in all cases but we aren't going to use the resulting pointer, so... + return other && ((*this == other) || (internal::try_cast(internal::meta_context::from(*ctx), fetch_node(), other.fetch_node().info->hash(), this) != nullptr)); + } + + /** + * @brief Checks whether a type supports conversion to another type. + * @param other The meta type to test for. + * @return True if the conversion is allowed, false otherwise. + */ + [[nodiscard]] bool can_convert(const meta_type &other) const noexcept { + if(const auto &to = other.info().hash(); (info().hash() == to) || ((fetch_node().conversion_helper != nullptr) && (other.is_arithmetic() || other.is_enum()))) { + return true; + } else if(const auto &from = fetch_node(); from.details) { + if(const auto *elem = internal::find_member<&internal::meta_conv_node::type>(from.details->conv, to); elem != nullptr) { + return true; + } + + for(auto &&curr: from.details->base) { + if(curr.type == to || meta_type{*ctx, curr.resolve(internal::meta_context::from(*ctx))}.can_convert(other)) { + return true; + } + } + } + + return false; + } + + /** + * @brief Returns a range to visit registered top-level base meta types. + * @return An iterable range to visit registered top-level base meta types. + */ + [[nodiscard]] meta_range base() const noexcept { + using range_type = meta_range; + return fetch_node().details ? range_type{{*ctx, fetch_node().details->base.cbegin()}, {*ctx, fetch_node().details->base.cend()}} : range_type{}; + } + + /** + * @brief Returns a range to visit registered top-level meta data. + * @return An iterable range to visit registered top-level meta data. + */ + [[nodiscard]] meta_range data() const noexcept { + using range_type = meta_range; + return fetch_node().details ? range_type{{*ctx, fetch_node().details->data.cbegin()}, {*ctx, fetch_node().details->data.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta data (bases are also visited). + * @param id Unique identifier. + * @param recursive True for a search in the base classes, false otherwise. + * @return The registered meta data for the given identifier, if any. + */ + [[nodiscard]] meta_data data(const id_type id, const bool recursive = true) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), fetch_node(), id, recursive); + return (elem != nullptr) ? meta_data{*ctx, *elem} : meta_data{}; + } + + /** + * @brief Returns a range to visit registered top-level functions. + * @return An iterable range to visit registered top-level functions. + */ + [[nodiscard]] meta_range func() const noexcept { + using return_type = meta_range; + return fetch_node().details ? return_type{{*ctx, fetch_node().details->func.cbegin()}, {*ctx, fetch_node().details->func.cend()}} : return_type{}; + } + + /** + * @brief Lookup utility for meta functions (bases are also visited). + * @param id Unique identifier. + * @param recursive True for a search in the base classes, false otherwise. + * @return The registered meta function for the given identifier, if any. + */ + [[nodiscard]] meta_func func(const id_type id, const bool recursive = true) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), fetch_node(), id, recursive); + return (elem != nullptr) ? meta_func{*ctx, *elem} : meta_func{}; + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { + if(const auto &ref = fetch_node(); ref.details) { + if(const auto *candidate = lookup(args, sz, false, [first = ref.details->ctor.cbegin(), last = ref.details->ctor.cend()]() mutable { return first == last ? nullptr : &*(first++); }); candidate) { + return candidate->invoke(*ctx, args); + } + } + + if(const auto &ref = fetch_node(); (sz == 0u) && (ref.default_constructor != nullptr)) { + return ref.default_constructor(*ctx); + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief construct + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&...args) const { + return construct(std::array{meta_any{*ctx, std::forward(args)}...}.data(), sizeof...(Args)); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + } + + /** + * @brief Wraps an opaque element of the underlying type. + * @param elem A valid pointer to an element of the underlying type. + * @param transfer_ownership True to transfer ownership, false otherwise. + * @return A wrapper that references the given instance. + */ + [[nodiscard]] meta_any from_void(void *elem, bool transfer_ownership = false) const { + return ((elem != nullptr) && (fetch_node().from_void != nullptr)) ? fetch_node().from_void(*ctx, elem, transfer_ownership ? elem : nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Wraps an opaque element of the underlying type. + * @param elem A valid pointer to an element of the underlying type. + * @return A wrapper that references the given instance. + */ + [[nodiscard]] meta_any from_void(const void *elem) const { + return ((elem != nullptr) && (fetch_node().from_void != nullptr)) ? fetch_node().from_void(*ctx, nullptr, elem) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Invokes a function given an identifier, if possible. + * @tparam Instance Type of instance to operate on. + * @param id Unique identifier. + * @param instance An instance that fits the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(const id_type id, Instance &&instance, meta_any *const args, const size_type sz) const { + meta_handle wrapped{*ctx, std::forward(instance)}; + + if(const auto &ref = fetch_node(); ref.details) { + if(auto *elem = internal::find_member<&internal::meta_func_node::id>(ref.details->func, id); elem != nullptr) { + if(const auto *candidate = lookup(args, sz, (wrapped->base().policy() == any_policy::cref), [curr = elem]() mutable { return (curr != nullptr) ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + return candidate->invoke(std::move(wrapped), args); + } + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.invoke(id, *wrapped.operator->(), args, sz); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @param id Unique identifier. + * @tparam Instance Type of instance to operate on. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An instance that fits the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(const id_type id, Instance &&instance, Args &&...args) const { + return invoke(id, std::forward(instance), std::array{meta_any{*ctx, std::forward(args)}...}.data(), sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * @tparam Instance Type of instance to operate on. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param instance An instance that fits the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + bool set(const id_type id, Instance &&instance, Type &&value) const { + const auto candidate = data(id); + return candidate && candidate.set(std::forward(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * @tparam Instance Type of instance to operate on. + * @param id Unique identifier. + * @param instance An instance that fits the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + template + [[nodiscard]] meta_any get(const id_type id, Instance &&instance) const { + const auto candidate = data(id); + return candidate ? candidate.get(std::forward(instance)) : meta_any{meta_ctx_arg, *ctx}; + } + + /*! @copydoc meta_data::traits */ + template + [[nodiscard]] Type traits() const noexcept { + return internal::meta_to_user_traits(fetch_node().traits); + } + + /*! @copydoc meta_data::custom */ + [[nodiscard]] meta_custom custom() const noexcept { + return fetch_node().custom; + } + + /*! @copydoc meta_data::operator bool */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_data::operator== */ + [[nodiscard]] bool operator==(const meta_type &other) const noexcept { + return (ctx == other.ctx) && (fetch_node().id == other.fetch_node().id); + } + +private: + mutable const internal::meta_type_node *node{}; + const meta_ctx *ctx{&locator::value_or()}; +}; + +/*! @copydoc operator!=(const meta_data &, const meta_data &) */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { + return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return *this ? meta_type{*ctx, fetch_node()} : meta_type{}; +} + +template +// NOLINTNEXTLINE(modernize-use-nodiscard) +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_any meta_any::allow_cast(const meta_type &type) const { + if(storage.has_value(type.info())) { + return as_ref(); + } else if(*this) { + if(const auto &from = fetch_node(); (from.conversion_helper != nullptr) && (type.is_arithmetic() || type.is_enum())) { + auto other = type.construct(); + const auto value = from.conversion_helper(nullptr, storage.data()); + other.fetch_node().conversion_helper(other.storage.data(), &value); + return other; + } + + if(const auto &from = fetch_node(); from.details) { + if(const auto *elem = internal::find_member<&internal::meta_conv_node::type>(from.details->conv, type.info().hash()); elem != nullptr) { + return elem->conv(*ctx, storage.data()); + } + + for(auto &&curr: from.details->base) { + if(auto other = curr.resolve(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.type == type.info().hash()) { + return other; + } else if(auto from_base = std::as_const(other).allow_cast(type); from_base) { + return from_base; + } + } + } + } + + return meta_any{meta_ctx_arg, *ctx}; +} + +[[nodiscard]] inline bool meta_any::allow_cast(const meta_type &type) { + if(storage.has_value(type.info())) { + return true; + } else if(auto other = std::as_const(*this).allow_cast(type); other) { + if(other.storage.owner()) { + std::swap(*this, other); + } + + return true; + } + + return false; +} + +inline bool meta_any::assign(const meta_any &other) { + if(!storage.assign(other.storage)) { + auto value = other.allow_cast(type()); + return storage.assign(value.storage); + } + + return true; +} + +inline bool meta_any::assign(meta_any &&other) { + return storage.assign(std::move(other.storage)) || storage.assign(std::as_const(other).allow_cast(type()).storage); +} + +[[nodiscard]] inline meta_type meta_data::type() const noexcept { + return meta_type{*ctx, node_or_assert().type(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { + return index < arity() ? node_or_assert().arg(*ctx, index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::ret() const noexcept { + return meta_type{*ctx, node_or_assert().ret(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { + return index < arity() ? node_or_assert().arg(*ctx, index) : meta_type{}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +class meta_sequence_container::meta_iterator final { + using vtable_type = void(const void *, const std::ptrdiff_t, meta_any *); + + template + static void basic_vtable(const void *value, const std::ptrdiff_t offset, meta_any *other) { + const auto &it = *static_cast(value); + other ? other->emplace(*it) : std::advance(const_cast(it), offset); + } + +public: + using value_type = meta_any; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::bidirectional_iterator_tag; + + meta_iterator() = default; + + template + meta_iterator(const meta_ctx &area, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + return vtable(handle.data(), 1, nullptr), *this; + } + + meta_iterator operator++(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), ++value, nullptr); + return orig; + } + + meta_iterator &operator--() noexcept { + return vtable(handle.data(), -1, nullptr), *this; + } + + meta_iterator operator--(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), --value, nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{meta_ctx_arg, *ctx}; + vtable(handle.data(), 0, &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return (vtable != nullptr); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] const any &base() const noexcept { + return handle; + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; + +[[nodiscard]] inline bool operator!=(const meta_sequence_container::iterator &lhs, const meta_sequence_container::iterator &rhs) noexcept { + return !(lhs == rhs); +} + +class meta_associative_container::meta_iterator final { + using vtable_type = void(const void *, std::pair *); + + template + static void basic_vtable(const void *value, std::pair *other) { + if(const auto &it = *static_cast(value); other) { + if constexpr(KeyOnly) { + other->first.emplace(*it); + } else { + other->first.emplacefirst))>(it->first); + other->second.emplacesecond))>(it->second); + } + } else { + ++const_cast(it); + } + } + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + meta_iterator() = default; + + template + meta_iterator(const meta_ctx &area, std::bool_constant, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + return vtable(handle.data(), nullptr), *this; + } + + meta_iterator operator++(int) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; + vtable(handle.data(), &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return (vtable != nullptr); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; + +[[nodiscard]] inline bool operator!=(const meta_associative_container::iterator &lhs, const meta_associative_container::iterator &rhs) noexcept { + return !(lhs == rhs); +} +/*! @endcond */ + +/** + * @brief Returns the meta value type of a container. + * @return The meta value type of the container. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { + return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @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 noexcept { + return size_fn(data); +} + +/** + * @brief Resizes a container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(const size_type sz) { + return !const_only && resize_fn(const_cast(data), sz); +} + +/** + * @brief Clears the content of a container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/** + * @brief Reserves storage for at least the given number of elements. + * @param sz The new capacity of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/** + * @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_end_fn(*ctx, const_only ? nullptr : const_cast(data), data, false); +} + +/** + * @brief Returns an iterator that is past the last element of a container. + * @return An iterator that is past the last element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { + return begin_end_fn(*ctx, const_only ? nullptr : const_cast(data), data, true); +} + +/** + * @brief Inserts an element at a specified location of a container. + * @param it Iterator before which the element will be inserted. + * @param value Element value to insert. + * @return A possibly invalid iterator to the inserted element. + */ +inline meta_sequence_container::iterator meta_sequence_container::insert(const iterator &it, meta_any value) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(const auto &vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) { + const bool is_value_type = (value.type().info() == *vtype.info); + return insert_fn(*ctx, const_cast(data), is_value_type ? value.base().data() : nullptr, is_value_type ? nullptr : value.base().data(), it); + } + + return iterator{}; +} + +/** + * @brief Removes a given element from a container. + * @param it Iterator to the element to remove. + * @return A possibly invalid iterator following the last removed element. + */ +inline meta_sequence_container::iterator meta_sequence_container::erase(const iterator &it) { + return const_only ? iterator{} : erase_fn(*ctx, const_cast(data), it); +} + +/** + * @brief Returns a reference to the element at a given location of a container. + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { + auto it = begin(); + it.operator++(static_cast(pos) - 1); + return *it; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { + return (data != nullptr); +} + +/** + * @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 noexcept { + return (key_type_node != nullptr) ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_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 noexcept { + return (mapped_type_node != nullptr) ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { + return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { + return size_fn(data); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/*! @copydoc meta_sequence_container::reserve */ +inline bool meta_associative_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return begin_end_fn(*ctx, const_only ? nullptr : const_cast(data), data, false); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return begin_end_fn(*ctx, const_only ? nullptr : const_cast(data), data, true); +} + +/** + * @brief Inserts a key-only or key/value element into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert, if needed. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) + && ((mapped_type_node == nullptr) || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))})) + && insert_fn(const_cast(data), key.base().data(), value.base().data()); +} + +/** + * @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 meta_associative_container::size_type meta_associative_container::erase(meta_any key) { + return (!const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})) ? erase_fn(const_cast(data), key.base().data()) : 0u; +} + +/** + * @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 key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast(data), data, key.base().data()) : iterator{}; +} + +/** + * @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 noexcept { + return (data != nullptr); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct sequence_container_extent: integral_constant {}; + +template +struct sequence_container_extent>>>: integral_constant> {}; + +template +inline constexpr std::size_t sequence_container_extent_v = sequence_container_extent::value; + +template +struct key_only_associative_container: std::true_type {}; + +template +struct key_only_associative_container>: std::false_type {}; + +template +inline constexpr bool key_only_associative_container_v = key_only_associative_container::value; + +template +struct reserve_aware_container: std::false_type {}; + +template +struct reserve_aware_container>: std::true_type {}; + +template +inline constexpr bool reserve_aware_container_v = reserve_aware_container::value; + +} // namespace internal +/*! @endcond */ + +/** + * @brief General purpose implementation of meta sequence container traits. + * @tparam Type Type of underlying sequence container. + */ +template +struct basic_meta_sequence_container_traits { + static_assert(std::is_same_v>>, "Unexpected type"); + + /*! @brief Unsigned integer type. */ + using size_type = typename meta_sequence_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = typename meta_sequence_container::iterator; + + /*! @brief Number of elements, or `meta_dynamic_extent` if dynamic. */ + static constexpr std::size_t extent = internal::sequence_container_extent_v; + /*! @brief True in case of fixed size containers, false otherwise. */ + [[deprecated("use ::extent instead")]] static constexpr bool fixed_size = (extent != meta_dynamic_extent); + + /** + * @brief Returns the number of elements in a container. + * @param container Opaque pointer to a container of the given type. + * @return Number of elements. + */ + [[nodiscard]] static size_type size(const void *container) { + return static_cast(container)->size(); + } + + /** + * @brief Clears a container. + * @param container Opaque pointer to a container of the given type. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool clear([[maybe_unused]] void *container) { + if constexpr(extent == meta_dynamic_extent) { + static_cast(container)->clear(); + return true; + } else { + return false; + } + } + + /** + * @brief Increases the capacity of a container. + * @param container Opaque pointer to a container of the given type. + * @param sz Desired capacity. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(internal::reserve_aware_container_v) { + static_cast(container)->reserve(sz); + return true; + } else { + return false; + } + } + + /** + * @brief Resizes a container. + * @param container Opaque pointer to a container of the given type. + * @param sz The new number of elements. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr((extent == meta_dynamic_extent) && std::is_default_constructible_v) { + static_cast(container)->resize(sz); + return true; + } else { + return false; + } + } + + /** + * @brief Returns a possibly const iterator to the beginning or the end. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @param end False to get a pointer that is past the last element. + * @return An iterator to the first or past the last element of the + * container. + */ + static iterator iter(const meta_ctx &area, void *container, const void *as_const, const bool end) { + return (container == nullptr) + ? iterator{area, end ? static_cast(as_const)->cend() : static_cast(as_const)->cbegin()} + : iterator{area, end ? static_cast(container)->end() : static_cast(container)->begin()}; + } + + /** + * @brief Assigns one element to a container and constructs its object from + * a given opaque instance. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param value Optional opaque instance of the object to construct (as + * value type). + * @param cref Optional opaque instance of the object to construct (as + * decayed const reference type). + * @param it Iterator before which the element will be inserted. + * @return A possibly invalid iterator to the inserted element. + */ + [[nodiscard]] static iterator insert([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) { + if constexpr(extent == meta_dynamic_extent) { + auto *const non_const = any_cast(&it.base()); + return {area, static_cast(container)->insert( + non_const ? *non_const : any_cast(it.base()), + (value != nullptr) ? *static_cast(value) : *static_cast *>(cref))}; + } else { + return iterator{}; + } + } + + /** + * @brief Erases an element from a container. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param it An opaque iterator to the element to erase. + * @return A possibly invalid iterator following the last removed element. + */ + [[nodiscard]] static iterator erase([[maybe_unused]] const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) { + if constexpr(extent == meta_dynamic_extent) { + auto *const non_const = any_cast(&it.base()); + return {area, static_cast(container)->erase(non_const ? *non_const : any_cast(it.base()))}; + } else { + return iterator{}; + } + } +}; + +/** + * @brief General purpose implementation of meta associative container traits. + * @tparam Type Type of underlying associative container. + */ +template +struct basic_meta_associative_container_traits { + static_assert(std::is_same_v>>, "Unexpected type"); + + /*! @brief Unsigned integer type. */ + using size_type = typename meta_associative_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = typename meta_associative_container::iterator; + + /*! @brief True in case of key-only containers, false otherwise. */ + static constexpr bool key_only = internal::key_only_associative_container_v; + + /** + * @brief Returns the number of elements in a container. + * @param container Opaque pointer to a container of the given type. + * @return Number of elements. + */ + [[nodiscard]] static size_type size(const void *container) { + return static_cast(container)->size(); + } + + /** + * @brief Clears a container. + * @param container Opaque pointer to a container of the given type. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool clear(void *container) { + static_cast(container)->clear(); + return true; + } + + /** + * @brief Increases the capacity of a container. + * @param container Opaque pointer to a container of the given type. + * @param sz Desired capacity. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(internal::reserve_aware_container_v) { + static_cast(container)->reserve(sz); + return true; + } else { + return false; + } + } + + /** + * @brief Returns a possibly const iterator to the beginning or the end. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @param end False to get a pointer that is past the last element. + * @return An iterator to the first or past the last element of the + * container. + */ + static iterator iter(const meta_ctx &area, void *container, const void *as_const, const bool end) { + return (container == nullptr) + ? iterator{area, std::bool_constant{}, end ? static_cast(as_const)->cend() : static_cast(as_const)->cbegin()} + : iterator{area, std::bool_constant{}, end ? static_cast(container)->end() : static_cast(container)->begin()}; + } + + /** + * @brief Inserts an element into a container, if the key does not exist. + * @param container Opaque pointer to a container of the given type. + * @param key An opaque key value of an element to insert. + * @param value Optional opaque value to insert (key-value containers). + * @return True if the insertion took place, false otherwise. + */ + [[nodiscard]] static bool insert(void *container, const void *key, [[maybe_unused]] const void *value) { + if constexpr(key_only) { + return static_cast(container)->insert(*static_cast(key)).second; + } else { + return static_cast(container)->emplace(*static_cast(key), *static_cast(value)).second; + } + } + + /** + * @brief Removes an element from a container. + * @param container Opaque pointer to a container of the given type. + * @param key An opaque key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + [[nodiscard]] static size_type erase(void *container, const void *key) { + return static_cast(container)->erase(*static_cast(key)); + } + + /** + * @brief Finds an element with a given key. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @param key Opaque key value of an element to search for. + * @return An iterator to the element with the given key, if any. + */ + static iterator find(const meta_ctx &area, void *container, const void *as_const, const void *key) { + return (container != nullptr) ? iterator{area, std::bool_constant{}, static_cast(container)->find(*static_cast(key))} + : iterator{area, std::bool_constant{}, static_cast(as_const)->find(*static_cast(key))}; + } +}; + +/** + * @brief Meta sequence container traits for `std::vector`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::array`s of any type. + * @tparam Type Template arguments for the container. + * @tparam N Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::list`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::deque`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::map`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::unordered_map`s of any + * type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::set`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::unordered_set`s of any + * type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `dense_map`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `dense_set`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +} // namespace entt + +#endif + +// #include "meta/context.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +#include +// #include "../container/dense_map.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/utility.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_type_node; + +struct meta_context { + dense_map, identity> value; + + [[nodiscard]] inline static meta_context &from(meta_ctx &); + [[nodiscard]] inline static const meta_context &from(const meta_ctx &); +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Disambiguation tag for constructors and the like. */ +class meta_ctx_arg_t final {}; + +/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ +inline constexpr meta_ctx_arg_t meta_ctx_arg{}; + +/*! @brief Opaque meta context type. */ +class meta_ctx: private internal::meta_context { + // attorney idiom like model to access the base class + friend struct internal::meta_context; +}; + +/*! @cond TURN_OFF_DOXYGEN */ +[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { + return ctx; +} + +[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { + return ctx; +} +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "meta/factory.hpp" +#ifndef ENTT_META_FACTORY_HPP +#define ENTT_META_FACTORY_HPP + +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/bit.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct fnv_1a_params; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 2166136261; + static constexpr auto prime = 16777619; +}; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 14695981039346656037ull; + static constexpr auto prime = 1099511628211ull; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr{}; + hash_type hash{fnv_1a_params<>::offset}; + size_type length{}; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using params = internal::fnv_1a_params<>; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const typename base_type::value_type *str) noexcept + : repr{str} {} + + const typename base_type::value_type *repr; + }; + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + [[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : basic_hashed_string{nullptr, 0u} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; base_type::length < len; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + for(; str[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + } + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{wrapper.repr} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; wrapper.repr[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(wrapper.repr[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Returns the size of a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] explicit constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_string operator""_hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_wstring operator""_hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "fwd.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_policy {}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_value_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v && !std::is_const_v>; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as auto_ policy. */ +struct as_is_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/** + * @brief Provides the member constant `value` to true if a type also is a meta + * policy, false otherwise. + * @tparam Type Type to check. + */ +template +struct is_meta_policy + : std::bool_constant> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template +inline constexpr bool is_meta_policy_v = is_meta_policy::value; + +} // namespace entt + +#endif + +// #include "range.hpp" + +// #include "resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include +// #include "../core/type_info.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @param ctx The context from which to search for meta types. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { + const auto &context = internal::meta_context::from(ctx); + return {ctx, internal::resolve>>(context)}; +} + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @param ctx The context from which to search for meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { + const auto &context = internal::meta_context::from(ctx); + return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param ctx The context from which to search for meta types. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { + for(auto &&curr: resolve(ctx)) { + if(curr.second.id() == id) { + return curr.second; + } + } + + return meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) noexcept { + return resolve(locator::value_or(), id); +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param ctx The context from which to search for meta types. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { + const auto &context = internal::meta_context::from(ctx); + const auto *elem = internal::try_resolve(context, info); + return (elem != nullptr) ? meta_type{ctx, *elem} : meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { + return resolve(locator::value_or(), info); +} + +} // namespace entt + +#endif + +// #include "utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../locator/locator.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/** + * @brief Meta function descriptor traits. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + * @tparam Static Function staticness. + * @tparam Const Function constness. + */ +template +struct meta_function_descriptor_traits { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = Args; + + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr bool is_static = Static; + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr bool is_const = Const; +}; + +/*! @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 + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + true> {}; + +/** + * @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 + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta data is associated. + * @tparam Class Actual owner of the data member. + * @tparam Ret Data member type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret &, + std::conditional_t, type_list<>, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam MaybeType First function argument. + * @tparam Args Other function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t< + std::is_same_v>, Type> || std::is_base_of_v>, Type>, + type_list, + type_list>, + !(std::is_same_v>, Type> || std::is_base_of_v>, Type>), + std::is_const_v> && (std::is_same_v>, Type> || std::is_base_of_v>, Type>)> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + type_list<>, + true, + false> {}; + +/** + * @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 Class::*); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Class); + +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 Wraps a value depending on the given policy. + * + * This function always returns a wrapped value in the requested context.
+ * Therefore, if the passed value is itself a wrapped object with a different + * context, it undergoes a rebinding to the requested context. + * + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param ctx The context from which to search for meta types. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { + if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; + } else if constexpr(std::is_same_v || (std::is_same_v && std::is_lvalue_reference_v)) { + return meta_any{ctx, std::in_place_type, value}; + } else if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type}; + } else { + return meta_any{ctx, std::forward(value)}; + } +} + +/** + * @brief Wraps a value depending on the given policy. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(Type &&value) { + return meta_dispatch(locator::value_or(), std::forward(value)); +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(std::is_void_v(candidate), args...))>) { + std::invoke(std::forward(candidate), args...); + return meta_any{ctx, std::in_place_type}; + } else { + return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); + } +} + +template +[[nodiscard]] meta_any meta_invoke(meta_any &instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, std::index_sequence) { + using descriptor = meta_function_helper_t>; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { + if(const auto *const clazz = instance.try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(instance.context(), std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { + if(auto *const clazz = instance.try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(instance.context(), std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else { + if(((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(instance.context(), std::forward(candidate), (args + Index)->cast>()...); + } + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + return meta_any{meta_ctx_arg, instance.context()}; +} + +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + if(((args + Index)->allow_cast() && ...)) { + return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + return meta_any{meta_ctx_arg, ctx}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param ctx The context from which to search for meta types. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { + const auto &context = internal::meta_context::from(ctx); + return {ctx, internal::meta_arg_node(context, Type{}, index)}; +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] meta_type meta_arg(const std::size_t index) noexcept { + return meta_arg(locator::value_or(), index); +} + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(std::is_member_function_pointer_v || std::is_function_v>>) { + using descriptor = meta_function_helper_t; + using data_type = type_list_element_t; + + if(auto *const clazz = instance->try_cast(); clazz && 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::return_type>; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz) = value.cast(); + return true; + } + } + } else if constexpr(std::is_pointer_v) { + 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]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { + if constexpr(std::is_member_pointer_v || std::is_function_v>>) { + if constexpr(!std::is_array_v>>>) { + if constexpr(std::is_invocable_v) { + if(auto *clazz = instance->try_cast(); clazz) { + return meta_dispatch(instance->context(), std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(instance->context(), std::invoke(Data, *fallback)); + } + } + } + + return meta_any{meta_ctx_arg, instance->context()}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{meta_ctx_arg, instance->context()}; + } else { + return meta_dispatch(instance->context(), *Data); + } + } else { + return meta_dispatch(instance->context(), Data); + } +} + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke(*instance.operator->(), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { + return internal::meta_invoke(*instance.operator->(), Candidate, args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param ctx The context from which to search for meta types. + * @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(const meta_ctx &ctx, meta_any *const args) { + return internal::meta_construct(ctx, args, std::index_sequence_for{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param ctx The context from which to search for meta types. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { + meta_any placeholder{meta_ctx_arg, ctx}; + return internal::meta_invoke(placeholder, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + return internal::meta_invoke(*args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); + } +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct(locator::value_or(), std::forward(candidate), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { + return meta_construct(ctx, Candidate, args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +class basic_meta_factory { + using invoke_type = std::remove_pointer_t; + + [[nodiscard]] auto &fetch_node() noexcept { + return *meta_context::from(*ctx).value[parent]; + } + + [[nodiscard]] auto *find_member_or_assert() { + auto *member = find_member<&meta_data_node::id>(fetch_node().details->data, bucket); + ENTT_ASSERT(member != nullptr, "Cannot find member"); + return member; + } + + [[nodiscard]] auto *find_overload_or_assert() { + auto *overload = find_overload(find_member<&meta_func_node::id>(fetch_node().details->func, bucket), invoke); + ENTT_ASSERT(overload != nullptr, "Cannot find overload"); + return overload; + } + + void reset_bucket(const id_type id, invoke_type *const ref = nullptr) { + invoke = ref; + bucket = id; + } + +protected: + void type(const id_type id, const char *name) noexcept { + reset_bucket(parent); + auto &elem = fetch_node(); + ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier"); + elem.name = name; + elem.id = id; + } + + template + void insert_or_assign(Type node) { + auto &elem = fetch_node(); + + reset_bucket(parent); + + if constexpr(std::is_same_v) { + auto *member = find_member<&meta_base_node::type>(elem.details->base, node.type); + member ? (*member = node) : elem.details->base.emplace_back(node); + } else if constexpr(std::is_same_v) { + auto *member = find_member<&meta_conv_node::type>(elem.details->conv, node.type); + member ? (*member = node) : elem.details->conv.emplace_back(node); + } else { + static_assert(std::is_same_v, "Unexpected type"); + auto *member = find_member<&meta_ctor_node::id>(elem.details->ctor, node.id); + member ? (*member = node) : elem.details->ctor.emplace_back(node); + } + } + + void data(meta_data_node node) { + auto &elem = fetch_node(); + + reset_bucket(node.id); + + if(auto *member = find_member<&meta_data_node::id>(elem.details->data, node.id); member == nullptr) { + elem.details->data.emplace_back(std::move(node)); + } else if(member->set != node.set || member->get != node.get) { + *member = std::move(node); + } + } + + void func(meta_func_node node) { + auto &elem = fetch_node(); + + reset_bucket(node.id, node.invoke); + + if(auto *member = find_member<&meta_func_node::id>(elem.details->func, node.id); member == nullptr) { + elem.details->func.emplace_back(std::move(node)); + } else if(auto *overload = find_overload(member, node.invoke); overload == nullptr) { + while(member->next != nullptr) { member = member->next.get(); } + member->next = std::make_unique(std::move(node)); + } + } + + void traits(const meta_traits value, const bool unset) { + auto set_or_unset_on = [=](auto &node) { + node.traits = (unset ? (node.traits & ~value) : (node.traits | value)); + }; + + if(bucket == parent) { + set_or_unset_on(fetch_node()); + } else if(invoke == nullptr) { + set_or_unset_on(*find_member_or_assert()); + } else { + set_or_unset_on(*find_overload_or_assert()); + } + } + + void custom(meta_custom_node node) { + if(bucket == parent) { + fetch_node().custom = std::move(node); + } else if(invoke == nullptr) { + find_member_or_assert()->custom = std::move(node); + } else { + find_overload_or_assert()->custom = std::move(node); + } + } + +public: + basic_meta_factory(meta_ctx &area, meta_type_node node) + : ctx{&area}, + parent{node.info->hash()}, + bucket{parent} { + if(auto *curr = meta_context::from(*ctx).value.try_emplace(parent, std::make_unique(std::move(node))).first->second.get(); curr->details == nullptr) { + curr->details = std::make_unique(); + } + } + +private: + meta_ctx *ctx{}; + id_type parent{}; + id_type bucket{}; + invoke_type *invoke{}; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Meta factory to be used for reflection purposes. + * @tparam Type Type for which the factory was created. + */ +template +class meta_factory: private internal::basic_meta_factory { + using base_type = internal::basic_meta_factory; + +public: + /*! @brief Type of object for which this factory builds a meta type. */ + using element_type = Type; + + /*! @brief Default constructor. */ + meta_factory() noexcept + : meta_factory{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context into which to construct meta types. + */ + meta_factory(meta_ctx &area) noexcept + : internal::basic_meta_factory{area, internal::setup_node_for()} {} + + /** + * @brief Assigns a custom unique identifier to a meta type. + * @param name A custom unique identifier as a **string literal**. + * @return A meta factory for the given type. + */ + meta_factory type(const char *name) noexcept { + return type(hashed_string::value(name), name); + } + + /** + * @brief Assigns a custom unique identifier to a meta type. + * @param id A custom unique identifier. + * @param name An optional name for the type as a **string literal**. + * @return A meta factory for the given type. + */ + meta_factory type(const id_type id, const char *name = nullptr) noexcept { + base_type::type(id, name); + return *this; + } + + /** + * @brief Assigns a meta base to a meta type. + * + * A reflected base class must be a real base class of the reflected type. + * + * @tparam Base Type of the base class to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + meta_factory base() noexcept { + static_assert(!std::is_same_v && std::is_base_of_v, "Invalid base type"); + auto *const op = +[](const void *instance) noexcept { return static_cast(static_cast(static_cast(instance))); }; + base_type::insert_or_assign(internal::meta_base_node{type_id().hash(), &internal::resolve, op}); + return *this; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * Conversion functions can be either free functions or member + * functions.
+ * In case of free functions, they must accept a const reference to an + * instance of the parent type as an argument. In case of member functions, + * they should have no arguments at all. + * + * @tparam Candidate The actual function to use for the conversion. + * @return A meta factory for the parent type. + */ + template + auto conv() noexcept { + using conv_type = std::remove_const_t>>; + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast(instance))); }; + base_type::insert_or_assign(internal::meta_conv_node{type_id().hash(), op}); + return *this; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * The given type must be such that an instance of the reflected type can be + * converted to it. + * + * @tparam To Type of the conversion function to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + meta_factory conv() noexcept { + using conv_type = std::remove_const_t>; + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast(*static_cast(instance))); }; + base_type::insert_or_assign(internal::meta_conv_node{type_id().hash(), op}); + return *this; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * Both member functions and free function can be assigned to meta types in + * the role of constructors. All that is required is that they return an + * instance of the underlying type.
+ * From a client's point of view, nothing changes if a constructor of a meta + * type is a built-in one or not. + * + * @tparam Candidate The actual function to use as a constructor. + * @tparam Policy Optional policy (no policy set by default). + * @return A meta factory for the parent type. + */ + template + meta_factory ctor() noexcept { + using descriptor = meta_function_helper_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + static_assert(std::is_same_v>, Type>, "The function doesn't return an object of the required type"); + base_type::insert_or_assign(internal::meta_ctor_node{type_id().hash(), descriptor::args_type::size, &meta_arg, &meta_construct}); + return *this; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * A meta constructor is uniquely identified by the types of its arguments + * and is such that there exists an actual constructor of the underlying + * type that can be invoked with parameters whose types are those given. + * + * @tparam Args Types of arguments to use to construct an instance. + * @return A meta factory for the parent type. + */ + template + meta_factory ctor() noexcept { + // default constructor is already implicitly generated, no need for redundancy + if constexpr(sizeof...(Args) != 0u) { + using descriptor = meta_function_helper_t; + base_type::insert_or_assign(internal::meta_ctor_node{type_id().hash(), descriptor::args_type::size, &meta_arg, &meta_construct}); + } + + return *this; + } + + /** + * @brief Assigns a meta data to a meta type. + * @tparam Data The actual variable to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param name A custom unique identifier as a **string literal**. + * @return A meta factory for the given type. + */ + template + meta_factory data(const char *name) noexcept { + return data(hashed_string::value(name), name); + } + + /** + * @brief Assigns a meta data to a meta type. + * + * Both data members and static and global variables, as well as constants + * of any kind, can be assigned to a meta type.
+ * From a client's point of view, all the variables associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Data The actual variable to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @param name An optional name for the meta data as a **string literal**. + * @return A meta factory for the parent type. + */ + template + meta_factory data(const id_type id, const char *name = nullptr) noexcept { + if constexpr(std::is_member_object_pointer_v) { + using data_type = std::invoke_result_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + base_type::data( + internal::meta_data_node{ + id, + name, + /* this is never static */ + std::is_const_v> ? internal::meta_traits::is_const : internal::meta_traits::is_none, + 1u, + &internal::resolve>>, + &meta_arg>>>, + &meta_setter, + &meta_getter}); + } else { + using data_type = std::remove_pointer_t; + + if constexpr(std::is_pointer_v) { + static_assert(Policy::template value, "Invalid return type for the given policy"); + } else { + static_assert(Policy::template value, "Invalid return type for the given policy"); + } + + base_type::data( + internal::meta_data_node{ + id, + name, + ((!std::is_pointer_v || std::is_const_v) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, + 1u, + &internal::resolve>>, + &meta_arg>>>, + &meta_setter, + &meta_getter}); + } + + return *this; + } + + /** + * @brief Assigns a meta data to a meta type by means of its setter and + * getter. + * @tparam Setter The actual function to use as a setter. + * @tparam Getter The actual function to use as a getter. + * @tparam Policy Optional policy (no policy set by default). + * @param name A custom unique identifier as a **string literal**. + * @return A meta factory for the given type. + */ + template + meta_factory data(const char *name) noexcept { + return data(hashed_string::value(name), name); + } + + /** + * @brief Assigns a meta data to a meta type by means of its setter and + * getter. + * + * Setters and getters can be either free functions, member functions or a + * mix of them.
+ * In case of free functions, setters and getters must accept a reference to + * an instance of the parent type as their first argument. A setter has then + * an extra argument of a type convertible to that of the parameter to + * set.
+ * In case of member functions, getters have no arguments at all, while + * setters has an argument of a type convertible to that of the parameter to + * set. + * + * @tparam Setter The actual function to use as a setter. + * @tparam Getter The actual function to use as a getter. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @param name An optional name for the meta data as a **string literal**. + * @return A meta factory for the parent type. + */ + template + meta_factory data(const id_type id, const char *name = nullptr) noexcept { + using descriptor = meta_function_helper_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + if constexpr(std::is_same_v) { + base_type::data( + internal::meta_data_node{ + id, + name, + /* this is never static */ + internal::meta_traits::is_const, + 0u, + &internal::resolve>>, + &meta_arg>, + &meta_setter, + &meta_getter}); + } else { + using args_type = typename meta_function_helper_t::args_type; + + base_type::data( + internal::meta_data_node{ + id, + name, + /* this is never static nor const */ + internal::meta_traits::is_none, + 1u, + &internal::resolve>>, + &meta_arg(args_type::size != 1u), args_type>>>, + &meta_setter, + &meta_getter}); + } + + return *this; + } + + /** + * @brief Assigns a meta function to a meta type. + * @tparam Candidate The actual function to attach to the meta function. + * @tparam Policy Optional policy (no policy set by default). + * @param name A custom unique identifier as a **string literal**. + * @return A meta factory for the given type. + */ + template + meta_factory func(const char *name) noexcept { + return func(hashed_string::value(name), name); + } + + /** + * @brief Assigns a meta function to a meta type. + * + * Both member functions and free functions can be assigned to a meta + * type.
+ * From a client's point of view, all the functions associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Candidate The actual function to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @param name An optional name for the function as a **string literal**. + * @return A meta factory for the parent type. + */ + template + meta_factory func(const id_type id, const char *name = nullptr) noexcept { + using descriptor = meta_function_helper_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + base_type::func( + internal::meta_func_node{ + id, + name, + (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none), + descriptor::args_type::size, + &internal::resolve, void, std::remove_const_t>>>, + &meta_arg, + &meta_invoke}); + + return *this; + } + + /** + * @brief Sets traits on the last created meta object. + * + * The assigned value must be an enum and intended as a bitmask. + * + * @tparam Value Type of the traits value. + * @param value Traits value. + * @param unset True to unset the given traits, false otherwise. + * @return A meta factory for the parent type. + */ + template + meta_factory traits(const Value value, const bool unset = false) { + static_assert(std::is_enum_v, "Invalid enum type"); + base_type::traits(internal::user_to_meta_traits(value), unset); + return *this; + } + + /** + * @brief Sets user defined data that will never be used by the library. + * @tparam Value Type of user defined data to store. + * @tparam Args Types of arguments to use to construct the user data. + * @param args Parameters to use to initialize the user data. + * @return A meta factory for the parent type. + */ + template + meta_factory custom(Args &&...args) { + base_type::custom(internal::meta_custom_node{type_id().hash(), std::make_shared(std::forward(args)...)}); + return *this; + } +}; + +/** + * @brief Resets a type and all its parts. + * + * Resets a type and all its data members, member functions and properties, as + * well as its constructors, destructors and conversion functions if any.
+ * Base classes aren't reset but the link between the two types is removed. + * + * The type is also removed from the set of searchable types. + * + * @param id Unique identifier. + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept { + auto &context = internal::meta_context::from(ctx); + + for(auto it = context.value.begin(); it != context.value.end();) { + if(it->second->id == id) { + it = context.value.erase(it); + } else { + ++it; + } + } +} + +/** + * @brief Resets a type and all its parts. + * + * Resets a type and all its data members, member functions and properties, as + * well as its constructors, destructors and conversion functions if any.
+ * Base classes aren't reset but the link between the two types is removed. + * + * The type is also removed from the set of searchable types. + * + * @param id Unique identifier. + */ +inline void meta_reset(const id_type id) noexcept { + meta_reset(locator::value_or(), id); +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + * @param ctx The context from which to reset meta types. + */ +template +void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.erase(type_id().hash()); +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + */ +template +void meta_reset() noexcept { + meta_reset(locator::value_or()); +} + +/** + * @brief Resets all meta types. + * + * @sa meta_reset + * + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.clear(); +} + +/** + * @brief Resets all meta types. + * + * @sa meta_reset + */ +inline void meta_reset() noexcept { + meta_reset(locator::value_or()); +} + +} // namespace entt + +#endif + +// #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/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "../locator/locator.hpp" + +// #include "adl_pointer.hpp" + +// #include "context.hpp" + +// #include "fwd.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_sequence_container() = default; + + /** + * @brief Context aware constructor. + * @tparam Type Type of container to wrap. + * @param area The context from which to search for meta types. + * @param instance The container to wrap. + */ + template + meta_sequence_container(const meta_ctx &area, Type &instance) noexcept + : ctx{&area}, + data{&instance}, + value_type_node{&internal::resolve}, + const_reference_node{&internal::resolve>>}, + size_fn{meta_sequence_container_traits>::size}, + clear_fn{meta_sequence_container_traits>::clear}, + reserve_fn{meta_sequence_container_traits>::reserve}, + resize_fn{meta_sequence_container_traits>::resize}, + begin_end_fn{meta_sequence_container_traits>::iter}, + insert_fn{meta_sequence_container_traits>::insert}, + erase_fn{meta_sequence_container_traits>::erase}, + const_only{std::is_const_v} {} + + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool resize(size_type); + inline bool clear(); + inline bool reserve(size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline iterator insert(const iterator &, meta_any); + inline iterator erase(const iterator &); + [[nodiscard]] inline meta_any operator[](size_type); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + const void *data{}; + const internal::meta_type_node &(*value_type_node)(const internal::meta_context &){}; + const internal::meta_type_node &(*const_reference_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + bool (*resize_fn)(void *, const size_type){}; + iterator (*begin_end_fn)(const meta_ctx &, void *, const void *, const bool){}; + iterator (*insert_fn)(const meta_ctx &, void *, const void *, const void *, const iterator &){}; + iterator (*erase_fn)(const meta_ctx &, void *, const iterator &){}; + bool const_only{}; +}; + +/*! @brief Proxy object for associative containers. */ +class meta_associative_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_associative_container() = default; + + /** + * @brief Context aware constructor. + * @tparam Type Type of container to wrap. + * @param area The context from which to search for meta types. + * @param instance The container to wrap. + */ + template + meta_associative_container(const meta_ctx &area, Type &instance) noexcept + : ctx{&area}, + data{&instance}, + key_type_node{&internal::resolve}, + value_type_node{&internal::resolve}, + size_fn{&meta_associative_container_traits>::size}, + clear_fn{&meta_associative_container_traits>::clear}, + reserve_fn{&meta_associative_container_traits>::reserve}, + begin_end_fn{&meta_associative_container_traits>::iter}, + insert_fn{&meta_associative_container_traits>::insert}, + erase_fn{&meta_associative_container_traits>::erase}, + find_fn{&meta_associative_container_traits>::find}, + const_only{std::is_const_v} { + if constexpr(!meta_associative_container_traits>::key_only) { + mapped_type_node = &internal::resolve; + } + } + + [[nodiscard]] inline meta_type key_type() const noexcept; + [[nodiscard]] inline meta_type mapped_type() const noexcept; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool clear(); + inline bool reserve(size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline size_type erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + const void *data{}; + const internal::meta_type_node &(*key_type_node)(const internal::meta_context &){}; + const internal::meta_type_node &(*mapped_type_node)(const internal::meta_context &){}; + const internal::meta_type_node &(*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + iterator (*begin_end_fn)(const meta_ctx &, void *, const void *, const bool){}; + bool (*insert_fn)(void *, const void *, const void *){}; + size_type (*erase_fn)(void *, const void *){}; + iterator (*find_fn)(const meta_ctx &, void *, const void *, const void *){}; + bool const_only{}; +}; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { + using vtable_type = void(const internal::meta_traits, const meta_any &, const void *); + + template + static void basic_vtable(const internal::meta_traits req, const meta_any &value, [[maybe_unused]] const void *other) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + + if(req == internal::meta_traits::is_none) { + value.node = &internal::resolve(internal::meta_context::from(*value.ctx)); + } + + if constexpr(is_meta_pointer_like_v) { + if(req == internal::meta_traits::is_pointer_like) { + if constexpr(std::is_function_v::element_type>) { + const_cast(value).emplace(*static_cast(other)); + } else if constexpr(!std::is_void_v::element_type>>) { + using in_place_type = decltype(adl_meta_pointer_like::dereference(std::declval())); + + if constexpr(std::is_constructible_v) { + if(const auto &pointer_like = *static_cast(other); pointer_like) { + const_cast(value).emplace(adl_meta_pointer_like::dereference(pointer_like)); + } + } else { + const_cast(value).emplace(adl_meta_pointer_like::dereference(*static_cast(other))); + } + } + } + } + + if constexpr(is_complete_v> || is_complete_v>) { + if(constexpr auto flag = (is_complete_v> ? internal::meta_traits::is_sequence_container : internal::meta_traits::is_associative_container); !!(req & flag)) { + using container_type = std::conditional_t>, meta_sequence_container, meta_associative_container>; + + if(!!(req & internal::meta_traits::is_const) || (value.storage.policy() == any_policy::cref)) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + *static_cast(const_cast(other)) = container_type{*value.ctx, any_cast(value.storage)}; + } else { + // NOLINTNEXTLINE(bugprone-casting-through-void) + *static_cast(const_cast(other)) = container_type{*value.ctx, any_cast(const_cast(value).storage)}; + } + } + } + } + + [[nodiscard]] const auto &fetch_node() const { + if(node == nullptr) { + ENTT_ASSERT(*this, "Invalid vtable function"); + vtable(internal::meta_traits::is_none, *this, nullptr); + } + + ENTT_ASSERT(node != nullptr, "Invalid pointer to node"); + return *node; + } + + meta_any(const meta_any &other, any elem) + : storage{std::move(elem)}, + ctx{other.ctx}, + node{other.node}, + vtable{other.vtable} {} + +public: + /*! Default constructor. */ + meta_any() = default; + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_any(meta_ctx_arg_t, const meta_ctx &area) + : ctx{&area} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(std::in_place_type_t, Args &&...args) + : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param area The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + ctx{&area}, + vtable{&basic_vtable>>} {} + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value A pointer to an object to take ownership of. + */ + template + explicit meta_any(std::in_place_t, Type *value) + : meta_any{locator::value_or(), std::in_place, value} {} + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value A pointer to an object to take ownership of. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_t, Type *value) + : storage{std::in_place, value}, + ctx{&area}, + vtable{storage ? &basic_vtable : nullptr} { + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(Type &&value) + : meta_any{locator::value_or(), std::forward(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(const meta_ctx &area, Type &&value) + : meta_any{area, std::in_place_type>, std::forward(value)} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_any(const meta_ctx &area, const meta_any &other) + : storage{other.storage}, + ctx{&area}, + node{(ctx == other.ctx) ? other.node : nullptr}, + vtable{other.vtable} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_any(const meta_ctx &area, meta_any &&other) + : storage{std::move(other.storage)}, + ctx{&area}, + node{(ctx == other.ctx) ? std::exchange(other.node, nullptr) : nullptr}, + vtable{std::exchange(other.vtable, nullptr)} {} + + /** + * @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) noexcept + : storage{std::move(other.storage)}, + ctx{other.ctx}, + node{std::exchange(other.node, nullptr)}, + vtable{std::exchange(other.vtable, nullptr)} {} + + /*! @brief Default destructor. */ + ~meta_any() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ + meta_any &operator=(const meta_any &other) { + if(this != &other) { + storage = other.storage; + ctx = other.ctx; + node = other.node; + vtable = other.vtable; + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any &operator=(meta_any &&other) noexcept { + storage = std::move(other.storage); + ctx = other.ctx; + node = std::exchange(other.node, nullptr); + vtable = std::exchange(other.vtable, 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>>> + meta_any &operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /*! @copydoc any::info */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(id_type id, Args &&...args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(id_type id, Args &&...args); + + /** + * @brief Sets the value of a given variable. + * @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(id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(id_type id) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(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 { + const auto *elem = any_cast(&storage); + return ((elem != nullptr) || !*this) ? elem : static_cast(internal::try_cast(internal::meta_context::from(*ctx), fetch_node(), type_hash>::value(), storage.data())); + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type *try_cast() { + return ((storage.policy() == any_policy::cref) && !std::is_const_v) ? nullptr : const_cast(std::as_const(*this).try_cast>()); + } + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A reference to the contained instance. + */ + template + [[nodiscard]] std::remove_const_t cast() const { + auto *const instance = try_cast>(); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); + } + + /*! @copydoc cast */ + template + [[nodiscard]] std::remove_const_t 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, "Invalid instance"); + return static_cast(*instance); + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return A valid meta object if convertible, an invalid one otherwise. + */ + [[nodiscard]] meta_any allow_cast(const meta_type &type) const; + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @param type Meta type to which the cast is requested. + * @return True if convertible, false otherwise. + */ + [[nodiscard]] bool allow_cast(const meta_type &type); + + /** + * @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 object if convertible, an invalid one otherwise. + */ + template + [[nodiscard]] meta_any allow_cast() const { + if constexpr(!std::is_reference_v || std::is_const_v>) { + if(storage.has_value>>()) { + return as_ref(); + } else if(*this) { + if constexpr(std::is_arithmetic_v>> || std::is_enum_v>>) { + if(const auto &from = fetch_node(); from.conversion_helper) { + return meta_any{*ctx, static_cast(from.conversion_helper(nullptr, storage.data()))}; + } + } + + if(const auto &from = fetch_node(); from.details != nullptr) { + if(const auto *elem = internal::find_member<&internal::meta_conv_node::type>(from.details->conv, entt::type_hash>>::value()); elem != nullptr) { + return elem->conv(*ctx, storage.data()); + } + + for(auto &&curr: from.details->base) { + if(auto other = curr.resolve(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.type == entt::type_hash>>::value()) { + return other; + } else if(auto from_base = std::as_const(other).template allow_cast(); from_base) { + return from_base; + } + } + } + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @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 convertible, false otherwise. + */ + template + [[nodiscard]] bool allow_cast() { + if constexpr(std::is_reference_v && !std::is_const_v>) { + return allow_cast &>() && (storage.policy() != any_policy::cref); + } else { + if(storage.has_value>>()) { + return true; + } else if(auto other = std::as_const(*this).allow_cast>>(); other) { + if(other.storage.owner()) { + std::swap(*this, other); + } + + return true; + } + + return false; + } + } + + /*! @copydoc any::emplace */ + template + void emplace(Args &&...args) { + storage.emplace(std::forward(args)...); + auto *prev = std::exchange(vtable, &basic_vtable>>); + node = (prev == vtable) ? node : nullptr; + } + + /*! @copydoc any::assign */ + bool assign(const meta_any &other); + + /*! @copydoc any::assign */ + bool assign(meta_any &&other); + + /*! @copydoc any::reset */ + void reset() { + storage.reset(); + node = nullptr; + vtable = nullptr; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { + meta_sequence_container proxy{}; + if(*this) { vtable(internal::meta_traits::is_sequence_container, *this, &proxy); } + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { + meta_sequence_container proxy{}; + if(*this) { vtable(internal::meta_traits::is_sequence_container | internal::meta_traits::is_const, *this, &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() noexcept { + meta_associative_container proxy{}; + if(*this) { vtable(internal::meta_traits::is_associative_container, *this, &proxy); } + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const noexcept { + meta_associative_container proxy{}; + if(*this) { vtable(internal::meta_traits::is_associative_container | internal::meta_traits::is_const, *this, &proxy); } + return proxy; + } + + /** + * @brief Indirection operator for dereferencing opaque objects. + * @return A wrapper that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; + if(*this) { vtable(internal::meta_traits::is_pointer_like, ret, storage.data()); } + return ret; + } + + /*! @copydoc any::operator bool */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(vtable == nullptr); + } + + /*! @copydoc any::operator== */ + [[nodiscard]] bool operator==(const meta_any &other) const noexcept { + return (ctx == other.ctx) && (!*this == !other) && (storage == other.storage); + } + + /*! @copydoc any::operator!= */ + [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { + return !(*this == other); + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() noexcept { + return meta_any{*this, storage.as_ref()}; + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() const noexcept { + return meta_any{*this, storage.as_ref()}; + } + + /** + * @brief Returns the underlying storage. + * @return The underlyig storage. + */ + [[nodiscard]] const any &base() const noexcept { + return storage; + } + + /** + * @brief Returns the underlying meta context. + * @return The underlying meta context. + */ + [[nodiscard]] const meta_ctx &context() const noexcept { + return *ctx; + } + +private: + any storage{}; + const meta_ctx *ctx{&locator::value_or()}; + mutable const internal::meta_type_node *node{}; + vtable_type *vtable{}; +}; + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @param ctx The context from which to search for meta types. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { + return meta_any{ctx, std::in_place_type, std::forward(value)}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(Type &&value) { + return forward_as_meta(locator::value_or(), std::forward(value)); +} + +/*! @brief Opaque pointers to instances of any type. */ +class meta_handle { + template, meta_any>>> + meta_handle(int, Type &value, Args &&...args) + : any{std::forward(args)..., value.as_ref()} {} + + template + meta_handle(char, Type &value, Args &&...args) + : any{std::forward(args)..., std::in_place_type, value} {} + +public: + /*! Default constructor. */ + 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 ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(const meta_ctx &ctx, Type &value) + : meta_handle{0, value, ctx} {} + + /** + * @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) + : meta_handle{0, value} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_handle(const meta_ctx &area, meta_handle &&other) + : any{area, std::move(other.any)} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + meta_handle(const meta_handle &) = delete; + + /*! @brief Default move constructor. */ + meta_handle(meta_handle &&) = default; + + /*! @brief Default destructor. */ + ~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 Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(any); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any *operator->() { + return &any; + } + + /*! @copydoc operator-> */ + [[deprecated("do not use const handles")]] [[nodiscard]] const meta_any *operator->() const { + return &any; + } + +private: + meta_any any{}; +}; + +/*! @brief Opaque wrapper for user defined data of any type. */ +struct meta_custom { + /*! @brief Default constructor. */ + meta_custom() noexcept = default; + + /** + * @brief Basic constructor for meta objects. + * @param curr The underlying node with which to construct the instance. + */ + meta_custom(const internal::meta_custom_node &curr) noexcept + : node{&curr} {} + + /** + * @brief Generic conversion operator. + * @tparam Type Type to which conversion is requested. + */ + template + [[nodiscard]] operator Type *() const noexcept { + return ((node != nullptr) && (type_hash>::value() == node->type)) ? static_cast(node->value.get()) : nullptr; + } + + /** + * @brief Generic conversion operator. + * @tparam Type Type to which conversion is requested. + */ + template + [[nodiscard]] operator Type &() const noexcept { + ENTT_ASSERT(static_cast(*this) != nullptr, "Invalid type"); + return *static_cast(node->value.get()); + } + +private: + const internal::meta_custom_node *node{}; +}; + +/*! @brief Opaque wrapper for data members. */ +class meta_data { + [[nodiscard]] auto &node_or_assert() const noexcept { + ENTT_ASSERT(node != nullptr, "Invalid pointer to node"); + return *node; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_data_node::size_type; + + /*! @brief Default constructor. */ + meta_data() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the name assigned to a data member, if any. + * @return The name assigned to the data member, if any. + */ + [[nodiscard]] const char *name() const noexcept { + return node_or_assert().name; + } + + /** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ + [[nodiscard]] size_type arity() const noexcept { + return node_or_assert().arity; + } + + /** + * @brief Indicates whether a data member is constant or not. + * @return True if the data member is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return !!(node_or_assert().traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a data member is static or not. + * @return True if the data member is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return !!(node_or_assert().traits & internal::meta_traits::is_static); + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Sets the value of a given variable. + * @tparam Instance Type of instance to operate on. + * @tparam Type Type of value to assign. + * @param instance An instance that fits the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + bool set(Instance &&instance, Type &&value) const { + return node_or_assert().set(meta_handle{*ctx, std::forward(instance)}, meta_any{*ctx, std::forward(value)}); + } + + /** + * @brief Gets the value of a given variable. + * @tparam Instance Type of instance to operate on. + * @param instance An instance that fits the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + template + [[nodiscard]] meta_any get(Instance &&instance) const { + return node_or_assert().get(meta_handle{*ctx, std::forward(instance)}); + } + + /** + * @brief Returns the type accepted by the i-th setter. + * @param index Index of the setter of which to return the accepted type. + * @return The type accepted by the i-th setter. + */ + [[nodiscard]] inline meta_type arg(size_type index) const noexcept; + + /** + * @brief Returns all meta traits for a given meta object. + * @tparam Type The type to convert the meta traits to. + * @return The registered meta traits, if any. + */ + template + [[nodiscard]] Type traits() const noexcept { + return internal::meta_to_user_traits(node_or_assert().traits); + } + + /** + * @brief Returns user defined data for a given meta object. + * @return User defined arbitrary data. + */ + [[nodiscard]] meta_custom custom() const noexcept { + return {node_or_assert().custom}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const 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_data &other) const noexcept { + return (ctx == other.ctx) && (node == other.node); + } + +private: + const internal::meta_data_node *node{}; + const meta_ctx *ctx{&locator::value_or()}; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for member functions. */ +class meta_func { + [[nodiscard]] auto &node_or_assert() const noexcept { + ENTT_ASSERT(node != nullptr, "Invalid pointer to node"); + return *node; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_func_node::size_type; + + /*! @brief Default constructor. */ + meta_func() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the name assigned to a member function, if any. + * @return The name assigned to the member function, if any. + */ + [[nodiscard]] const char *name() const noexcept { + return node_or_assert().name; + } + + /** + * @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 noexcept { + return node_or_assert().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 noexcept { + return !!(node_or_assert().traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a member function is static or not. + * @return True if the member function is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return !!(node_or_assert().traits & internal::meta_traits::is_static); + } + + /** + * @brief Returns the return type of a member function. + * @return The return type of the member function. + */ + [[nodiscard]] inline meta_type ret() const 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 noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * @tparam Instance Type of instance to operate on. + * @param instance An instance that fits the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(Instance &&instance, meta_any *const args, const size_type sz) const { + return (sz == arity()) ? node_or_assert().invoke(meta_handle{*ctx, std::forward(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @tparam Instance Type of instance to operate on. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An instance that fits the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(Instance &&instance, Args &&...args) const { + return invoke(std::forward(instance), std::array{meta_any{*ctx, std::forward(args)}...}.data(), sizeof...(Args)); + } + + /*! @copydoc meta_data::traits */ + template + [[nodiscard]] Type traits() const noexcept { + return internal::meta_to_user_traits(node_or_assert().traits); + } + + /*! @copydoc meta_data::custom */ + [[nodiscard]] meta_custom custom() const noexcept { + return {node_or_assert().custom}; + } + + /** + * @brief Returns the next overload of a given function, if any. + * @return The next overload of the given function, if any. + */ + [[nodiscard]] meta_func next() const { + return (node_or_assert().next != nullptr) ? meta_func{*ctx, *node_or_assert().next} : meta_func{}; + } + + /*! @copydoc meta_data::operator bool */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_data::operator== */ + [[nodiscard]] bool operator==(const meta_func &other) const noexcept { + return (ctx == other.ctx) && (node == other.node); + } + +private: + const internal::meta_func_node *node{}; + const meta_ctx *ctx{&locator::value_or()}; +}; + +/*! @copydoc operator!=(const meta_data &, const meta_data &) */ +[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for types. */ +class meta_type { + [[nodiscard]] const auto &fetch_node() const { + return (node == nullptr) ? internal::resolve(internal::meta_context::from(*ctx)) : *node; + } + + template + [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { + decltype(next()) candidate = nullptr; + size_type same{}; + bool ambiguous{}; + + for(auto curr = next(); curr; curr = next()) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(constness && !(curr->traits & internal::meta_traits::is_const)) { + continue; + } + } + + if(curr->arity == sz) { + size_type match{}; + size_type pos{}; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + for(; pos < sz && args[pos]; ++pos) { + const auto other = curr->arg(*ctx, pos); + const auto type = args[pos].type(); + + if(const auto &info = other.info(); info == type.info()) { + ++match; + } else if(!(type.fetch_node().conversion_helper && other.fetch_node().conversion_helper) && !(type.fetch_node().details && (internal::find_member<&internal::meta_base_node::type>(type.fetch_node().details->base, info.hash()) || internal::find_member<&internal::meta_conv_node::type>(type.fetch_node().details->conv, info.hash())))) { + break; + } + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + if(pos == sz) { + if(!candidate || match > same) { + candidate = curr; + same = match; + ambiguous = false; + } else if(match == same) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(!!(curr->traits & internal::meta_traits::is_const) != !!(candidate->traits & internal::meta_traits::is_const)) { + candidate = !!(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; + ambiguous = false; + continue; + } + } + + ambiguous = true; + } + } + } + } + + return ambiguous ? nullptr : candidate; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + + /*! @brief Default constructor. */ + meta_type() noexcept = default; + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept + : meta_type{area, curr.resolve(internal::meta_context::from(area))} {} + + /** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *fetch_node().info; + } + + /** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ + [[nodiscard]] id_type id() const noexcept { + return fetch_node().id; + } + + /** + * @brief Returns the name assigned to a type, if any. + * @return The name assigned to the type, if any. + */ + [[nodiscard]] const char *name() const noexcept { + return fetch_node().name; + } + + /** + * @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 noexcept { + return fetch_node().size_of; + } + + /** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ + [[nodiscard]] bool is_arithmetic() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_arithmetic); + } + + /** + * @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 noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_integral); + } + + /** + * @brief Checks whether a type refers to a signed type or not. + * @return True if the underlying type is a signed type, false otherwise. + */ + [[nodiscard]] bool is_signed() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_signed); + } + + /** + * @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 noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_array); + } + + /** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ + [[nodiscard]] bool is_enum() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_enum); + } + + /** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ + [[nodiscard]] bool is_class() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_class); + } + + /** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ + [[nodiscard]] bool is_pointer() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_pointer); + } + + /** + * @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 noexcept { + return meta_type{*ctx, fetch_node().remove_pointer(internal::meta_context::from(*ctx))}; + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is pointer-like, false otherwise. + */ + [[nodiscard]] bool is_pointer_like() const noexcept { + return !!(fetch_node().traits & internal::meta_traits::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 noexcept { + return !!(fetch_node().traits & internal::meta_traits::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 noexcept { + return !!(fetch_node().traits & internal::meta_traits::is_associative_container); + } + + /** + * @brief Checks whether a type refers to a template specialization or not. + * @return True if the type is a template specialization, false otherwise. + */ + [[nodiscard]] bool is_template_specialization() const noexcept { + return (fetch_node().templ.arity != 0u); + } + + /** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ + [[nodiscard]] size_type template_arity() const noexcept { + return fetch_node().templ.arity; + } + + /** + * @brief Returns a tag for the class template of the underlying type. + * @return The tag for the class template of the underlying type. + */ + [[nodiscard]] meta_type template_type() const noexcept { + return (fetch_node().templ.resolve != nullptr) ? meta_type{*ctx, fetch_node().templ.resolve(internal::meta_context::from(*ctx))} : 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]] meta_type template_arg(const size_type index) const noexcept { + return index < template_arity() ? meta_type{*ctx, fetch_node().templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; + } + + /** + * @brief Checks if a type supports direct casting to another type. + * @param other The meta type to test for. + * @return True if direct casting is allowed, false otherwise. + */ + [[nodiscard]] bool can_cast(const meta_type &other) const noexcept { + // casting this is UB in all cases but we aren't going to use the resulting pointer, so... + return other && ((*this == other) || (internal::try_cast(internal::meta_context::from(*ctx), fetch_node(), other.fetch_node().info->hash(), this) != nullptr)); + } + + /** + * @brief Checks whether a type supports conversion to another type. + * @param other The meta type to test for. + * @return True if the conversion is allowed, false otherwise. + */ + [[nodiscard]] bool can_convert(const meta_type &other) const noexcept { + if(const auto &to = other.info().hash(); (info().hash() == to) || ((fetch_node().conversion_helper != nullptr) && (other.is_arithmetic() || other.is_enum()))) { + return true; + } else if(const auto &from = fetch_node(); from.details) { + if(const auto *elem = internal::find_member<&internal::meta_conv_node::type>(from.details->conv, to); elem != nullptr) { + return true; + } + + for(auto &&curr: from.details->base) { + if(curr.type == to || meta_type{*ctx, curr.resolve(internal::meta_context::from(*ctx))}.can_convert(other)) { + return true; + } + } + } + + return false; + } + + /** + * @brief Returns a range to visit registered top-level base meta types. + * @return An iterable range to visit registered top-level base meta types. + */ + [[nodiscard]] meta_range base() const noexcept { + using range_type = meta_range; + return fetch_node().details ? range_type{{*ctx, fetch_node().details->base.cbegin()}, {*ctx, fetch_node().details->base.cend()}} : range_type{}; + } + + /** + * @brief Returns a range to visit registered top-level meta data. + * @return An iterable range to visit registered top-level meta data. + */ + [[nodiscard]] meta_range data() const noexcept { + using range_type = meta_range; + return fetch_node().details ? range_type{{*ctx, fetch_node().details->data.cbegin()}, {*ctx, fetch_node().details->data.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta data (bases are also visited). + * @param id Unique identifier. + * @param recursive True for a search in the base classes, false otherwise. + * @return The registered meta data for the given identifier, if any. + */ + [[nodiscard]] meta_data data(const id_type id, const bool recursive = true) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), fetch_node(), id, recursive); + return (elem != nullptr) ? meta_data{*ctx, *elem} : meta_data{}; + } + + /** + * @brief Returns a range to visit registered top-level functions. + * @return An iterable range to visit registered top-level functions. + */ + [[nodiscard]] meta_range func() const noexcept { + using return_type = meta_range; + return fetch_node().details ? return_type{{*ctx, fetch_node().details->func.cbegin()}, {*ctx, fetch_node().details->func.cend()}} : return_type{}; + } + + /** + * @brief Lookup utility for meta functions (bases are also visited). + * @param id Unique identifier. + * @param recursive True for a search in the base classes, false otherwise. + * @return The registered meta function for the given identifier, if any. + */ + [[nodiscard]] meta_func func(const id_type id, const bool recursive = true) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), fetch_node(), id, recursive); + return (elem != nullptr) ? meta_func{*ctx, *elem} : meta_func{}; + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { + if(const auto &ref = fetch_node(); ref.details) { + if(const auto *candidate = lookup(args, sz, false, [first = ref.details->ctor.cbegin(), last = ref.details->ctor.cend()]() mutable { return first == last ? nullptr : &*(first++); }); candidate) { + return candidate->invoke(*ctx, args); + } + } + + if(const auto &ref = fetch_node(); (sz == 0u) && (ref.default_constructor != nullptr)) { + return ref.default_constructor(*ctx); + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief construct + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&...args) const { + return construct(std::array{meta_any{*ctx, std::forward(args)}...}.data(), sizeof...(Args)); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) + } + + /** + * @brief Wraps an opaque element of the underlying type. + * @param elem A valid pointer to an element of the underlying type. + * @param transfer_ownership True to transfer ownership, false otherwise. + * @return A wrapper that references the given instance. + */ + [[nodiscard]] meta_any from_void(void *elem, bool transfer_ownership = false) const { + return ((elem != nullptr) && (fetch_node().from_void != nullptr)) ? fetch_node().from_void(*ctx, elem, transfer_ownership ? elem : nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Wraps an opaque element of the underlying type. + * @param elem A valid pointer to an element of the underlying type. + * @return A wrapper that references the given instance. + */ + [[nodiscard]] meta_any from_void(const void *elem) const { + return ((elem != nullptr) && (fetch_node().from_void != nullptr)) ? fetch_node().from_void(*ctx, nullptr, elem) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Invokes a function given an identifier, if possible. + * @tparam Instance Type of instance to operate on. + * @param id Unique identifier. + * @param instance An instance that fits the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(const id_type id, Instance &&instance, meta_any *const args, const size_type sz) const { + meta_handle wrapped{*ctx, std::forward(instance)}; + + if(const auto &ref = fetch_node(); ref.details) { + if(auto *elem = internal::find_member<&internal::meta_func_node::id>(ref.details->func, id); elem != nullptr) { + if(const auto *candidate = lookup(args, sz, (wrapped->base().policy() == any_policy::cref), [curr = elem]() mutable { return (curr != nullptr) ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + return candidate->invoke(std::move(wrapped), args); + } + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.invoke(id, *wrapped.operator->(), args, sz); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @param id Unique identifier. + * @tparam Instance Type of instance to operate on. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An instance that fits the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(const id_type id, Instance &&instance, Args &&...args) const { + return invoke(id, std::forward(instance), std::array{meta_any{*ctx, std::forward(args)}...}.data(), sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * @tparam Instance Type of instance to operate on. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param instance An instance that fits the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + bool set(const id_type id, Instance &&instance, Type &&value) const { + const auto candidate = data(id); + return candidate && candidate.set(std::forward(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * @tparam Instance Type of instance to operate on. + * @param id Unique identifier. + * @param instance An instance that fits the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + template + [[nodiscard]] meta_any get(const id_type id, Instance &&instance) const { + const auto candidate = data(id); + return candidate ? candidate.get(std::forward(instance)) : meta_any{meta_ctx_arg, *ctx}; + } + + /*! @copydoc meta_data::traits */ + template + [[nodiscard]] Type traits() const noexcept { + return internal::meta_to_user_traits(fetch_node().traits); + } + + /*! @copydoc meta_data::custom */ + [[nodiscard]] meta_custom custom() const noexcept { + return fetch_node().custom; + } + + /*! @copydoc meta_data::operator bool */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_data::operator== */ + [[nodiscard]] bool operator==(const meta_type &other) const noexcept { + return (ctx == other.ctx) && (fetch_node().id == other.fetch_node().id); + } + +private: + mutable const internal::meta_type_node *node{}; + const meta_ctx *ctx{&locator::value_or()}; +}; + +/*! @copydoc operator!=(const meta_data &, const meta_data &) */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { + return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return *this ? meta_type{*ctx, fetch_node()} : meta_type{}; +} + +template +// NOLINTNEXTLINE(modernize-use-nodiscard) +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_any meta_any::allow_cast(const meta_type &type) const { + if(storage.has_value(type.info())) { + return as_ref(); + } else if(*this) { + if(const auto &from = fetch_node(); (from.conversion_helper != nullptr) && (type.is_arithmetic() || type.is_enum())) { + auto other = type.construct(); + const auto value = from.conversion_helper(nullptr, storage.data()); + other.fetch_node().conversion_helper(other.storage.data(), &value); + return other; + } + + if(const auto &from = fetch_node(); from.details) { + if(const auto *elem = internal::find_member<&internal::meta_conv_node::type>(from.details->conv, type.info().hash()); elem != nullptr) { + return elem->conv(*ctx, storage.data()); + } + + for(auto &&curr: from.details->base) { + if(auto other = curr.resolve(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.type == type.info().hash()) { + return other; + } else if(auto from_base = std::as_const(other).allow_cast(type); from_base) { + return from_base; + } + } + } + } + + return meta_any{meta_ctx_arg, *ctx}; +} + +[[nodiscard]] inline bool meta_any::allow_cast(const meta_type &type) { + if(storage.has_value(type.info())) { + return true; + } else if(auto other = std::as_const(*this).allow_cast(type); other) { + if(other.storage.owner()) { + std::swap(*this, other); + } + + return true; + } + + return false; +} + +inline bool meta_any::assign(const meta_any &other) { + if(!storage.assign(other.storage)) { + auto value = other.allow_cast(type()); + return storage.assign(value.storage); + } + + return true; +} + +inline bool meta_any::assign(meta_any &&other) { + return storage.assign(std::move(other.storage)) || storage.assign(std::as_const(other).allow_cast(type()).storage); +} + +[[nodiscard]] inline meta_type meta_data::type() const noexcept { + return meta_type{*ctx, node_or_assert().type(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { + return index < arity() ? node_or_assert().arg(*ctx, index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::ret() const noexcept { + return meta_type{*ctx, node_or_assert().ret(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { + return index < arity() ? node_or_assert().arg(*ctx, index) : meta_type{}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +class meta_sequence_container::meta_iterator final { + using vtable_type = void(const void *, const std::ptrdiff_t, meta_any *); + + template + static void basic_vtable(const void *value, const std::ptrdiff_t offset, meta_any *other) { + const auto &it = *static_cast(value); + other ? other->emplace(*it) : std::advance(const_cast(it), offset); + } + +public: + using value_type = meta_any; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::bidirectional_iterator_tag; + + meta_iterator() = default; + + template + meta_iterator(const meta_ctx &area, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + return vtable(handle.data(), 1, nullptr), *this; + } + + meta_iterator operator++(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), ++value, nullptr); + return orig; + } + + meta_iterator &operator--() noexcept { + return vtable(handle.data(), -1, nullptr), *this; + } + + meta_iterator operator--(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), --value, nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{meta_ctx_arg, *ctx}; + vtable(handle.data(), 0, &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return (vtable != nullptr); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] const any &base() const noexcept { + return handle; + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; + +[[nodiscard]] inline bool operator!=(const meta_sequence_container::iterator &lhs, const meta_sequence_container::iterator &rhs) noexcept { + return !(lhs == rhs); +} + +class meta_associative_container::meta_iterator final { + using vtable_type = void(const void *, std::pair *); + + template + static void basic_vtable(const void *value, std::pair *other) { + if(const auto &it = *static_cast(value); other) { + if constexpr(KeyOnly) { + other->first.emplace(*it); + } else { + other->first.emplacefirst))>(it->first); + other->second.emplacesecond))>(it->second); + } + } else { + ++const_cast(it); + } + } + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + meta_iterator() = default; + + template + meta_iterator(const meta_ctx &area, std::bool_constant, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + return vtable(handle.data(), nullptr), *this; + } + + meta_iterator operator++(int) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; + vtable(handle.data(), &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return (vtable != nullptr); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; + +[[nodiscard]] inline bool operator!=(const meta_associative_container::iterator &lhs, const meta_associative_container::iterator &rhs) noexcept { + return !(lhs == rhs); +} +/*! @endcond */ + +/** + * @brief Returns the meta value type of a container. + * @return The meta value type of the container. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { + return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @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 noexcept { + return size_fn(data); +} + +/** + * @brief Resizes a container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(const size_type sz) { + return !const_only && resize_fn(const_cast(data), sz); +} + +/** + * @brief Clears the content of a container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/** + * @brief Reserves storage for at least the given number of elements. + * @param sz The new capacity of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/** + * @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_end_fn(*ctx, const_only ? nullptr : const_cast(data), data, false); +} + +/** + * @brief Returns an iterator that is past the last element of a container. + * @return An iterator that is past the last element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { + return begin_end_fn(*ctx, const_only ? nullptr : const_cast(data), data, true); +} + +/** + * @brief Inserts an element at a specified location of a container. + * @param it Iterator before which the element will be inserted. + * @param value Element value to insert. + * @return A possibly invalid iterator to the inserted element. + */ +inline meta_sequence_container::iterator meta_sequence_container::insert(const iterator &it, meta_any value) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(const auto &vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) { + const bool is_value_type = (value.type().info() == *vtype.info); + return insert_fn(*ctx, const_cast(data), is_value_type ? value.base().data() : nullptr, is_value_type ? nullptr : value.base().data(), it); + } + + return iterator{}; +} + +/** + * @brief Removes a given element from a container. + * @param it Iterator to the element to remove. + * @return A possibly invalid iterator following the last removed element. + */ +inline meta_sequence_container::iterator meta_sequence_container::erase(const iterator &it) { + return const_only ? iterator{} : erase_fn(*ctx, const_cast(data), it); +} + +/** + * @brief Returns a reference to the element at a given location of a container. + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { + auto it = begin(); + it.operator++(static_cast(pos) - 1); + return *it; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { + return (data != nullptr); +} + +/** + * @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 noexcept { + return (key_type_node != nullptr) ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_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 noexcept { + return (mapped_type_node != nullptr) ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { + return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { + return size_fn(data); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/*! @copydoc meta_sequence_container::reserve */ +inline bool meta_associative_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return begin_end_fn(*ctx, const_only ? nullptr : const_cast(data), data, false); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return begin_end_fn(*ctx, const_only ? nullptr : const_cast(data), data, true); +} + +/** + * @brief Inserts a key-only or key/value element into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert, if needed. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) + && ((mapped_type_node == nullptr) || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))})) + && insert_fn(const_cast(data), key.base().data(), value.base().data()); +} + +/** + * @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 meta_associative_container::size_type meta_associative_container::erase(meta_any key) { + return (!const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})) ? erase_fn(const_cast(data), key.base().data()) : 0u; +} + +/** + * @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 key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast(data), data, key.base().data()) : iterator{}; +} + +/** + * @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 noexcept { + return (data != nullptr); +} + +} // namespace entt + +#endif + +// #include "meta/node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/bit.hpp" + +// #include "../core/enum.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "context.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; +class meta_handle; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class meta_traits : std::uint32_t { + is_none = 0x0000, + is_const = 0x0001, + is_static = 0x0002, + is_arithmetic = 0x0004, + is_integral = 0x0008, + is_signed = 0x0010, + is_array = 0x0020, + is_enum = 0x0040, + is_class = 0x0080, + is_pointer = 0x0100, + is_pointer_like = 0x0200, + is_sequence_container = 0x0400, + is_associative_container = 0x0800, + _user_defined_traits = 0xFFFF, + _entt_enum_as_bitmask = 0xFFFF +}; + +template +[[nodiscard]] auto meta_to_user_traits(const meta_traits traits) noexcept { + static_assert(std::is_enum_v, "Invalid enum type"); + constexpr auto shift = popcount(static_cast>(meta_traits::_user_defined_traits)); + return Type{static_cast>(static_cast>(traits) >> shift)}; +} + +template +[[nodiscard]] auto user_to_meta_traits(const Type value) noexcept { + static_assert(std::is_enum_v, "Invalid enum type"); + constexpr auto shift = popcount(static_cast>(meta_traits::_user_defined_traits)); + const auto traits = static_cast>(static_cast>(value)); + ENTT_ASSERT(traits < ((~static_cast>(meta_traits::_user_defined_traits)) >> shift), "Invalid traits"); + return meta_traits{traits << shift}; +} + +struct meta_type_node; + +struct meta_custom_node { + id_type type{}; + std::shared_ptr value{}; +}; + +struct meta_base_node { + id_type type{}; + const meta_type_node &(*resolve)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; +}; + +struct meta_conv_node { + id_type type{}; + meta_any (*conv)(const meta_ctx &, const void *){}; +}; + +struct meta_ctor_node { + using size_type = std::size_t; + + id_type id{}; + size_type arity{0u}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_any *const){}; +}; + +struct meta_data_node { + using size_type = std::size_t; + + id_type id{}; + const char *name{}; + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + const meta_type_node &(*type)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + bool (*set)(meta_handle, meta_any){}; + meta_any (*get)(meta_handle){}; + meta_custom_node custom{}; +}; + +struct meta_func_node { + using size_type = std::size_t; + + id_type id{}; + const char *name{}; + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + const meta_type_node &(*ret)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(meta_handle, meta_any *const){}; + std::unique_ptr next; + meta_custom_node custom{}; +}; + +struct meta_template_node { + using size_type = std::size_t; + + size_type arity{0u}; + const meta_type_node &(*resolve)(const meta_context &) noexcept {}; + const meta_type_node &(*arg)(const meta_context &, const size_type) noexcept {}; +}; + +struct meta_type_descriptor { + std::vector ctor{}; + std::vector base{}; + std::vector conv{}; + std::vector data{}; + std::vector func{}; +}; + +struct meta_type_node { + using size_type = std::size_t; + + const type_info *info{}; + id_type id{}; + const char *name{}; + meta_traits traits{meta_traits::is_none}; + size_type size_of{0u}; + const meta_type_node &(*remove_pointer)(const meta_context &) noexcept {}; + meta_any (*default_constructor)(const meta_ctx &){}; + double (*conversion_helper)(void *, const void *){}; + meta_any (*from_void)(const meta_ctx &, void *, const void *){}; + meta_template_node templ{}; + meta_custom_node custom{}; + std::unique_ptr details{}; +}; + +template +[[nodiscard]] auto *find_member(Type &from, const Value value) { + for(auto &&elem: from) { + if((elem.*Member) == value) { + return &elem; + } + } + + return static_cast(nullptr); +} + +[[nodiscard]] inline auto *find_overload(meta_func_node *curr, std::remove_pointer_t *const ref) { + while((curr != nullptr) && (curr->invoke != ref)) { curr = curr->next.get(); } + return curr; +} + +template +[[nodiscard]] auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id, bool recursive) { + using value_type = typename std::remove_reference_t*Member))>::value_type; + + if(node.details) { + if(auto *member = find_member<&value_type::id>((node.details.get()->*Member), id); member != nullptr) { + return member; + } + + if(recursive) { + for(auto &&curr: node.details->base) { + if(auto *elem = look_for(context, curr.resolve(context), id, recursive); elem) { + return elem; + } + } + } + } + + return static_cast(nullptr); +} + +template +const meta_type_node &resolve(const meta_context &) noexcept; + +template +[[nodiscard]] const meta_type_node &meta_arg_node(const meta_context &context, type_list, const std::size_t index) noexcept { + using resolve_type = const meta_type_node &(*)(const meta_context &) noexcept; + constexpr std::array list{&resolve>>...}; + ENTT_ASSERT(index < sizeof...(Args), "Out of bounds"); + return list[index](context); +} + +[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const id_type to, const void *instance) noexcept { + if(from.details) { + for(auto &&curr: from.details->base) { + if(const void *other = curr.cast(instance); curr.type == to) { + return other; + } else if(const void *elem = try_cast(context, curr.resolve(context), to, other); elem) { + return elem; + } + } + } + + return nullptr; +} + +template +auto setup_node_for() noexcept { + meta_type_node node{ + &type_id(), + type_id().hash(), + nullptr, + (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) + | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) + | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) + | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) + | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) + | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) + | (std::is_pointer_v ? meta_traits::is_pointer : meta_traits::is_none) + | (is_meta_pointer_like_v ? meta_traits::is_pointer_like : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_sequence_container : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_associative_container : meta_traits::is_none), + size_of_v, + &resolve>>}; + + if constexpr(std::is_default_constructible_v) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, std::in_place_type}; + }; + } + + if constexpr(std::is_arithmetic_v) { + node.conversion_helper = +[](void *lhs, const void *rhs) { + return lhs ? static_cast(*static_cast(lhs) = static_cast(*static_cast(rhs))) : static_cast(*static_cast(rhs)); + }; + } else if constexpr(std::is_enum_v) { + node.conversion_helper = +[](void *lhs, const void *rhs) { + return lhs ? static_cast(*static_cast(lhs) = static_cast(static_cast>(*static_cast(rhs)))) : static_cast(*static_cast(rhs)); + }; + } + + if constexpr(!std::is_void_v && !std::is_function_v) { + node.from_void = +[](const meta_ctx &ctx, void *elem, const void *celem) { + if(elem && celem) { // ownership construction request + return meta_any{ctx, std::in_place, static_cast *>(elem)}; + } + + if(elem) { // non-const reference construction request + return meta_any{ctx, std::in_place_type &>, *static_cast *>(elem)}; + } + + // const reference construction request + return meta_any{ctx, std::in_place_type &>, *static_cast *>(celem)}; + }; + } + + if constexpr(is_complete_v>) { + node.templ = meta_template_node{ + meta_template_traits::args_type::size, + &resolve::class_type>, + +[](const meta_context &area, const std::size_t index) noexcept -> decltype(auto) { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; + } + + return node; +} + +[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { + const auto it = context.value.find(info.hash()); + return (it != context.value.end()) ? it->second.get() : nullptr; +} + +template +[[nodiscard]] const meta_type_node &resolve(const meta_context &context) noexcept { + static_assert(std::is_same_v>>, "Invalid type"); + static const meta_type_node node = setup_node_for(); + const auto *elem = try_resolve(context, *node.info); + return (elem == nullptr) ? node : *elem; +} + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "meta/pointer.hpp" +// IWYU pragma: always_keep + +#ifndef ENTT_META_POINTER_HPP +#define ENTT_META_POINTER_HPP + +#include +#include +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @brief Makes plain pointers pointer-like types for the meta system. + * @tparam Type Element type. + */ +template +struct is_meta_pointer_like + : std::true_type {}; + +/** + * @brief Partial specialization used to reject pointers to arrays. + * @tparam Type Type of elements of the array. + * @tparam N Number of elements of the array. + */ +template +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +struct is_meta_pointer_like + : std::false_type {}; + +/** + * @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + */ +template +struct is_meta_pointer_like> + : std::true_type {}; + +/** + * @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + * @tparam Args Other arguments. + */ +template +struct is_meta_pointer_like> + : std::true_type {}; + +/** + * @brief Specialization for self-proclaimed meta pointer like types. + * @tparam Type Element type. + */ +template +struct is_meta_pointer_like> + : std::true_type {}; + +} // namespace entt + +#endif + +// #include "meta/policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_policy {}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_value_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v && !std::is_const_v>; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as auto_ policy. */ +struct as_is_t final: private internal::meta_policy { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/** + * @brief Provides the member constant `value` to true if a type also is a meta + * policy, false otherwise. + * @tparam Type Type to check. + */ +template +struct is_meta_policy + : std::bool_constant> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template +inline constexpr bool is_meta_policy_v = is_meta_policy::value; + +} // namespace entt + +#endif + +// #include "meta/range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "context.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_base_node; + +template +struct meta_range_iterator final { + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr meta_range_iterator() noexcept + : it{}, + ctx{} {} + + constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept + : it{iter}, + ctx{&area} {} + + constexpr meta_range_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr meta_range_iterator operator++(int) noexcept { + const meta_range_iterator orig = *this; + return ++(*this), orig; + } + + constexpr meta_range_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr meta_range_iterator operator--(int) noexcept { + const meta_range_iterator orig = *this; + return operator--(), orig; + } + + constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr meta_range_iterator operator+(const difference_type value) const noexcept { + meta_range_iterator copy = *this; + return (copy += value); + } + + constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr meta_range_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + if constexpr(std::is_same_v) { + return {it[value].first, Type{*ctx, *it[value].second}}; + } else if constexpr(std::is_same_v::value_type, meta_base_node>) { + return {it[value].type, Type{*ctx, it[value]}}; + } else { + return {it[value].id, Type{*ctx, it[value]}}; + } + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; + +private: + It it; + const meta_ctx *ctx; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam It Type of forward iterator. + */ +template +using meta_range = iterable_adaptor>; + +} // namespace entt + +#endif + +// #include "meta/resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include +// #include "../core/type_info.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @param ctx The context from which to search for meta types. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { + const auto &context = internal::meta_context::from(ctx); + return {ctx, internal::resolve>>(context)}; +} + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @param ctx The context from which to search for meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { + const auto &context = internal::meta_context::from(ctx); + return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param ctx The context from which to search for meta types. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { + for(auto &&curr: resolve(ctx)) { + if(curr.second.id() == id) { + return curr.second; + } + } + + return meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) noexcept { + return resolve(locator::value_or(), id); +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param ctx The context from which to search for meta types. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { + const auto &context = internal::meta_context::from(ctx); + const auto *elem = internal::try_resolve(context, info); + return (elem != nullptr) ? meta_type{ctx, *elem} : meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { + return resolve(locator::value_or(), info); +} + +} // namespace entt + +#endif + +// #include "meta/template.hpp" +// IWYU pragma: always_keep + +#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 class> +struct meta_class_template_tag {}; + +/** + * @brief General purpose traits class for generating meta template information. + * @tparam Clazz Type of class template. + * @tparam Args Types of template arguments. + */ +template class 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; +}; + +} // namespace entt + +#endif + +// #include "meta/type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include +#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 given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + */ +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; + +} // namespace entt + +#endif + +// #include "meta/utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../locator/locator.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/** + * @brief Meta function descriptor traits. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + * @tparam Static Function staticness. + * @tparam Const Function constness. + */ +template +struct meta_function_descriptor_traits { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = Args; + + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr bool is_static = Static; + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr bool is_const = Const; +}; + +/*! @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 + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + true> {}; + +/** + * @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 + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta data is associated. + * @tparam Class Actual owner of the data member. + * @tparam Ret Data member type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret &, + std::conditional_t, type_list<>, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam MaybeType First function argument. + * @tparam Args Other function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t< + std::is_same_v>, Type> || std::is_base_of_v>, Type>, + type_list, + type_list>, + !(std::is_same_v>, Type> || std::is_base_of_v>, Type>), + std::is_const_v> && (std::is_same_v>, Type> || std::is_base_of_v>, Type>)> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + type_list<>, + true, + false> {}; + +/** + * @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 Class::*); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Class); + +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 Wraps a value depending on the given policy. + * + * This function always returns a wrapped value in the requested context.
+ * Therefore, if the passed value is itself a wrapped object with a different + * context, it undergoes a rebinding to the requested context. + * + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param ctx The context from which to search for meta types. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { + if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; + } else if constexpr(std::is_same_v || (std::is_same_v && std::is_lvalue_reference_v)) { + return meta_any{ctx, std::in_place_type, value}; + } else if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type}; + } else { + return meta_any{ctx, std::forward(value)}; + } +} + +/** + * @brief Wraps a value depending on the given policy. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(Type &&value) { + return meta_dispatch(locator::value_or(), std::forward(value)); +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(std::is_void_v(candidate), args...))>) { + std::invoke(std::forward(candidate), args...); + return meta_any{ctx, std::in_place_type}; + } else { + return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); + } +} + +template +[[nodiscard]] meta_any meta_invoke(meta_any &instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, std::index_sequence) { + using descriptor = meta_function_helper_t>; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { + if(const auto *const clazz = instance.try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(instance.context(), std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { + if(auto *const clazz = instance.try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(instance.context(), std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else { + if(((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(instance.context(), std::forward(candidate), (args + Index)->cast>()...); + } + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + return meta_any{meta_ctx_arg, instance.context()}; +} + +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + if(((args + Index)->allow_cast() && ...)) { + return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + return meta_any{meta_ctx_arg, ctx}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param ctx The context from which to search for meta types. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { + const auto &context = internal::meta_context::from(ctx); + return {ctx, internal::meta_arg_node(context, Type{}, index)}; +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] meta_type meta_arg(const std::size_t index) noexcept { + return meta_arg(locator::value_or(), index); +} + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(std::is_member_function_pointer_v || std::is_function_v>>) { + using descriptor = meta_function_helper_t; + using data_type = type_list_element_t; + + if(auto *const clazz = instance->try_cast(); clazz && 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::return_type>; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz) = value.cast(); + return true; + } + } + } else if constexpr(std::is_pointer_v) { + 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]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { + if constexpr(std::is_member_pointer_v || std::is_function_v>>) { + if constexpr(!std::is_array_v>>>) { + if constexpr(std::is_invocable_v) { + if(auto *clazz = instance->try_cast(); clazz) { + return meta_dispatch(instance->context(), std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(instance->context(), std::invoke(Data, *fallback)); + } + } + } + + return meta_any{meta_ctx_arg, instance->context()}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{meta_ctx_arg, instance->context()}; + } else { + return meta_dispatch(instance->context(), *Data); + } + } else { + return meta_dispatch(instance->context(), Data); + } +} + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke(*instance.operator->(), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { + return internal::meta_invoke(*instance.operator->(), Candidate, args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param ctx The context from which to search for meta types. + * @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(const meta_ctx &ctx, meta_any *const args) { + return internal::meta_construct(ctx, args, std::index_sequence_for{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param ctx The context from which to search for meta types. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { + meta_any placeholder{meta_ctx_arg, ctx}; + return internal::meta_invoke(placeholder, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and std::span) + return internal::meta_invoke(*args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); + } +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct(locator::value_or(), std::forward(candidate), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { + return meta_construct(ctx, Candidate, args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +} // namespace entt + +#endif + +// #include "poly/poly.hpp" +#ifndef ENTT_POLY_POLY_HPP +#define ENTT_POLY_POLY_HPP + +#include +#include +#include +#include +#include +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct fnv_1a_params; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 2166136261; + static constexpr auto prime = 16777619; +}; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 14695981039346656037ull; + static constexpr auto prime = 1099511628211ull; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr{}; + hash_type hash{fnv_1a_params<>::offset}; + size_type length{}; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using params = internal::fnv_1a_params<>; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const typename base_type::value_type *str) noexcept + : repr{str} {} + + const typename base_type::value_type *repr; + }; + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + [[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : basic_hashed_string{nullptr, 0u} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; base_type::length < len; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + for(; str[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + } + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{wrapper.repr} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; wrapper.repr[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(wrapper.repr[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Returns the size of a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] explicit constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_string operator""_hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_wstring operator""_hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr const char *pretty_function() noexcept { +#if defined ENTT_PRETTY_FUNCTION + return static_cast(ENTT_PRETTY_FUNCTION); +#else + return ""; +#endif +} + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + const std::string_view full_name{pretty_function()}; + auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static const type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_request : std::uint8_t { + info, + transfer, + assign, + compare, + copy, + move +}; + +template +struct basic_any_storage { + static constexpr bool has_buffer = true; + union { + const void *instance{}; + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + alignas(Align) std::byte buffer[Len]; + }; +}; + +template +struct basic_any_storage<0u, Align> { + static constexpr bool has_buffer = false; + const void *instance{}; +}; + +template +// NOLINTNEXTLINE(bugprone-sizeof-expression) +struct in_situ: std::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v> {}; + +template +struct in_situ: std::false_type {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any: private internal::basic_any_storage { + using request = internal::any_request; + using base_type = internal::basic_any_storage; + using vtable_type = const void *(const request, const basic_any &, const void *); + using deleter_type = void(const basic_any &); + + template + static constexpr bool in_situ_v = internal::in_situ::value; + + template + static const void *basic_vtable(const request req, const basic_any &value, const void *other) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + + switch(const auto *elem = static_cast(value.data()); req) { + case request::info: + return &type_id(); + case request::transfer: + if constexpr(std::is_move_assignable_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + *const_cast(elem) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case request::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(elem) = *static_cast(other); + return other; + } + break; + case request::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return (*elem == *static_cast(other)) ? other : nullptr; + } else { + return (elem == other) ? other : nullptr; + } + case request::copy: + if constexpr(std::is_copy_constructible_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + static_cast(const_cast(other))->initialize(*elem); + } + break; + case request::move: + ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy"); + if constexpr(in_situ_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion) + return ::new(&static_cast(const_cast(other))->buffer) Type{std::move(*const_cast(elem))}; + } + } + + return nullptr; + } + + template + static void basic_deleter(const basic_any &value) { + static_assert(std::is_same_v>, Type>, "Invalid type"); + ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !std::is_trivially_destructible_v), "Unexpected policy"); + + const auto *elem = static_cast(value.data()); + + if constexpr(in_situ_v) { + (value.mode == any_policy::embedded) ? elem->~Type() : (delete elem); + } else if constexpr(std::is_array_v) { + delete[] elem; + } else { + delete elem; + } + } + + template + void initialize([[maybe_unused]] Args &&...args) { + using plain_type = std::remove_const_t>; + + vtable = basic_vtable; + underlying_type = type_hash::value(); + + if constexpr(std::is_void_v) { + deleter = nullptr; + mode = any_policy::empty; + this->instance = nullptr; + } else if constexpr(std::is_lvalue_reference_v) { + deleter = nullptr; + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) + this->instance = (std::addressof(args), ...); + } else if constexpr(in_situ_v) { + if constexpr(std::is_trivially_destructible_v) { + deleter = nullptr; + } else { + deleter = &basic_deleter; + } + + mode = any_policy::embedded; + + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + ::new(&this->buffer) plain_type{std::forward(args)...}; + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + ::new(&this->buffer) plain_type(std::forward(args)...); + } + } else { + deleter = &basic_deleter; + mode = any_policy::dynamic; + + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + this->instance = new plain_type{std::forward(args)...}; + } else if constexpr(std::is_array_v) { + static_assert(sizeof...(Args) == 0u, "Invalid arguments"); + this->instance = new plain_type[std::extent_v](); + } else { + this->instance = new plain_type(std::forward(args)...); + } + } + } + + void invoke_deleter_if_exists() { + if(deleter != nullptr) { + deleter(*this); + } + } + +public: + /*! @brief Size of the internal buffer. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : base_type{} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value A pointer to an object to take ownership of. + */ + template + explicit basic_any(std::in_place_t, Type *value) + : base_type{} { + static_assert(!std::is_const_v && !std::is_void_v, "Non-const non-void pointer required"); + + if(value == nullptr) { + initialize(); + } else { + initialize(*value); + deleter = &basic_deleter; + mode = any_policy::dynamic; + } + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, 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{} { + other.vtable(request::copy, other, this); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : base_type{}, + vtable{other.vtable}, + deleter{other.deleter}, + underlying_type{other.underlying_type}, + mode{other.mode} { + if(other.mode == any_policy::embedded) { + other.vtable(request::move, other, this); + } else if(other.mode != any_policy::empty) { + this->instance = std::exchange(other.instance, nullptr); + } + } + + /*! @brief Frees the internal buffer, whatever it means. */ + ~basic_any() { + invoke_deleter_if_exists(); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + if(this != &other) { + invoke_deleter_if_exists(); + + if(other) { + other.vtable(request::copy, other, this); + } else { + initialize(); + } + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + if(this != &other) { + invoke_deleter_if_exists(); + + if(other.mode == any_policy::embedded) { + other.vtable(request::move, other, this); + } else if(other.mode != any_policy::empty) { + this->instance = std::exchange(other.instance, nullptr); + } + + vtable = other.vtable; + deleter = other.deleter; + underlying_type = other.underlying_type; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template, basic_any>>> + basic_any &operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] bool has_value() const noexcept { + return (mode != any_policy::empty); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @param req Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + [[nodiscard]] bool has_value(const type_info &req) const noexcept { + return (underlying_type == req.hash()); + } + + /** + * @brief Returns false if the wrapper does not contain the expected type, + * true otherwise. + * @tparam Type Expected type. + * @return False if the wrapper does not contain the expected type, true + * otherwise. + */ + template + [[nodiscard]] bool has_value() const noexcept { + static_assert(std::is_same_v, Type>, "Invalid type"); + return (underlying_type == type_hash::value()); + } + + /** + * @brief Returns the object type info if any, `type_id()` otherwise. + * @return The object type info if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *static_cast(vtable(request::info, *this, nullptr)); + } + + /*! @copydoc info */ + [[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept { + return info(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + if constexpr(base_type::has_buffer) { + return (mode == any_policy::embedded) ? &this->buffer : this->instance; + } else { + return this->instance; + } + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return has_value(req) ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @tparam Type Expected type. + * @return An opaque pointer the contained instance, if any. + */ + template + [[nodiscard]] const Type *data() const noexcept { + return has_value>() ? static_cast(data()) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @tparam Type Expected type. + * @return An opaque pointer the contained instance, if any. + */ + template + [[nodiscard]] Type *data() noexcept { + if constexpr(std::is_const_v) { + return std::as_const(*this).template data>(); + } else { + return (mode == any_policy::cref) ? nullptr : const_cast(std::as_const(*this).template data>()); + } + } + + /** + * @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) { + invoke_deleter_if_exists(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) { + return (vtable(request::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) + bool assign(basic_any &&other) { + if(other && (mode != any_policy::cref) && (underlying_type == other.underlying_type)) { + return (other.mode == any_policy::cref) ? (vtable(request::assign, *this, std::as_const(other).data()) != nullptr) : (vtable(request::transfer, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @brief Destroys contained object */ + void reset() { + invoke_deleter_if_exists(); + initialize(); + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return has_value(); + } + + /** + * @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 basic_any &other) const noexcept { + if(other && (underlying_type == other.underlying_type)) { + return (vtable(request::compare, *this, other.data()) != nullptr); + } + + return (!*this && !other); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + basic_any other = std::as_const(*this).as_ref(); + other.mode = (mode == any_policy::cref ? any_policy::cref : any_policy::ref); + return other; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + basic_any other{}; + other.instance = data(); + other.vtable = vtable; + other.underlying_type = underlying_type; + other.mode = any_policy::cref; + return other; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::dynamic || mode == any_policy::embedded); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + vtable_type *vtable{}; + deleter_type *deleter{}; + id_type underlying_type{}; + any_policy mode{}; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] std::remove_const_t any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] std::remove_const_t any_cast(basic_any &data) 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, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) +[[nodiscard]] std::remove_const_t any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } + + return any_cast(data); + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + return data->template data>(); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + return data->template data(); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the buffer reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr const char *pretty_function() noexcept { +#if defined ENTT_PRETTY_FUNCTION + return static_cast(ENTT_PRETTY_FUNCTION); +#else + return ""; +#endif +} + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + const std::string_view full_name{pretty_function()}; + auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static const type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_POLY_FWD_HPP +#define ENTT_POLY_FWD_HPP + +#include + +namespace entt { + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +template +class basic_poly; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Concept Concept descriptor. + */ +template +using poly = basic_poly; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Inspector class used to infer the type of the virtual table. */ +struct poly_inspector { + /** + * @brief Generic conversion operator (definition only). + * @tparam Type Type to which conversion is requested. + */ + template + 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 + [[nodiscard]] poly_inspector invoke(Args &&...args) const; + + /*! @copydoc invoke */ + template + [[nodiscard]] 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 (*)(Clazz &, Args...)) + -> std::enable_if_t, inspector>, Ret (*)(constness_as_t, Clazz> &, Args...)>; + + template + static auto vtable_entry(Ret (*)(Args...)) + -> Ret (*)(const basic_any &, Args...); + + template + static auto vtable_entry(Ret (Clazz::*)(Args...)) + -> std::enable_if_t, Ret (*)(basic_any &, Args...)>; + + template + static auto vtable_entry(Ret (Clazz::*)(Args...) const) + -> std::enable_if_t, Ret (*)(const basic_any &, Args...)>; + + template + static auto make_vtable(value_list) noexcept + -> decltype(std::make_tuple(vtable_entry(Candidate)...)); + + template + [[nodiscard]] static constexpr auto make_vtable(type_list) noexcept { + if constexpr(sizeof...(Func) == 0u) { + 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...)) noexcept { + 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) noexcept { + vtable_type impl{}; + (fill_vtable_entry>>(std::get(impl)), ...); + return impl; + } + + using vtable_type = decltype(make_vtable(Concept{})); + static constexpr bool is_mono = std::tuple_size_v == 1u; + +public: + /*! @brief Virtual table type. */ + using type = std::conditional_t, const vtable_type *>; + + /** + * @brief Returns a static virtual table for a specific concept and type. + * @tparam Type The type for which to generate the virtual table. + * @return A static virtual table for the given concept and type. + */ + template + [[nodiscard]] static type instance() noexcept { + static_assert(std::is_same_v>, "Type differs from its decayed form"); + static const vtable_type vtable = fill_vtable(std::make_index_sequence::size>{}); + + if constexpr(is_mono) { + return std::get<0>(vtable); + } else { + return &vtable; + } + } +}; + +/** + * @brief Poly base class used to inject functionalities into concepts. + * @tparam Poly The outermost poly class. + */ +template +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); + + if constexpr(std::is_function_v>) { + return poly.vtable(poly.storage, std::forward(args)...); + } else { + 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); + + if constexpr(std::is_function_v>) { + static_assert(Member == 0u, "Unknown member"); + return poly.vtable(poly.storage, std::forward(args)...); + } else { + 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>> { + friend struct poly_base; + +public: + /*! @brief Concept type. */ + using concept_type = typename Concept::template type>; + /*! @brief Virtual table type. */ + using vtable_type = typename poly_vtable::type; + + /*! @brief Default constructor. */ + basic_poly() noexcept = default; + + /** + * @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 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) noexcept + : basic_poly{std::in_place_type>>, std::forward(value)} {} + + /** + * @brief Returns the object type info if any, `type_id()` otherwise. + * @return The object type info if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &info() const noexcept { + return storage.info(); + } + + /*! @copydoc info */ + [[deprecated("use ::info instead")]] [[nodiscard]] const type_info &type() const noexcept { + return info(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return storage.data(); + } + + /*! @copydoc data */ + [[nodiscard]] void *data() 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) { + storage.template emplace(std::forward(args)...); + vtable = poly_vtable::template instance>>(); + } + + /*! @brief Destroys contained object */ + void reset() { + storage.reset(); + vtable = {}; + } + + /** + * @brief Returns false if a poly is empty, true otherwise. + * @return False if the poly is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(storage); + } + + /** + * @brief Returns a pointer to the underlying concept. + * @return A pointer to the underlying concept. + */ + [[nodiscard]] concept_type *operator->() noexcept { + return this; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const concept_type *operator->() const noexcept { + return this; + } + + /** + * @brief Aliasing constructor. + * @return A poly that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_poly as_ref() noexcept { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_poly as_ref() const noexcept { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + +private: + basic_any storage{}; + vtable_type vtable{}; +}; + +} // namespace entt + +#endif + +// #include "process/process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include +#include +#include +#include +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_PROCESS_FWD_HPP +#define ENTT_PROCESS_FWD_HPP + +#include +#include + +namespace entt { + +template> +class basic_process; + +/*! @brief Alias declaration for the most common use case. */ +using process = basic_process; + +template> +class basic_scheduler; + +/*! @brief Alias declaration for the most common use case. */ +using scheduler = basic_scheduler; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct process_adaptor; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Base class for processes. + * + * Derived classes must specify what's the intended type for elapsed times.
+ * A process can implement the following member functions whether required: + * + * * @code{.cpp} + * void update(Delta, void *) override; + * @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 succeeded() override; + * @endcode + * + * It's invoked in case of success, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void failed() override; + * @endcode + * + * It's invoked in case of errors, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void aborted() override; + * @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` member functions and even pause or unpause the process + * itself. + * + * @sa scheduler + * + * @tparam Delta Type to use to provide elapsed time. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_process: public std::enable_shared_from_this> { + enum class state : std::uint8_t { + idle = 0, + running, + paused, + succeeded, + failed, + aborted, + finished, + rejected + }; + + virtual void update(const Delta, void *) { + abort(); + } + + virtual void succeeded() {} + virtual void failed() {} + virtual void aborted() {} + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + + /*! @brief Default constructor. */ + basic_process() + : basic_process{allocator_type{}} {} + + /** + * @brief Constructs a scheduler with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_process(const allocator_type &allocator) + : next{nullptr, allocator}, + current{state::idle} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_process(const basic_process &) = delete; + + /*! @brief Default move constructor, deleted on purpose. */ + basic_process(basic_process &&) = delete; + + /*! @brief Default destructor. */ + virtual ~basic_process() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This process scheduler. + */ + basic_process &operator=(const basic_process &) = delete; + + /** + * @brief Default move assignment operator, deleted on purpose. + * @return This process scheduler. + */ + basic_process &operator=(basic_process &&) = delete; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return next.second(); + } + + /*! @brief Aborts a process if it's still alive, otherwise does nothing. */ + void abort() { + if(alive()) { + current = state::aborted; + } + } + + /** + * @brief Terminates a process with success if it's still alive, otherwise + * does nothing. + */ + void succeed() noexcept { + if(alive()) { + current = state::succeeded; + } + } + + /** + * @brief Terminates a process with errors if it's still alive, otherwise + * does nothing. + */ + void fail() noexcept { + if(alive()) { + current = state::failed; + } + } + + /*! @brief Stops a process if it's running, otherwise does nothing. */ + void pause() noexcept { + if(alive()) { + current = state::paused; + } + } + + /*! @brief Restarts a process if it's paused, otherwise does nothing. */ + void unpause() noexcept { + if(alive()) { + current = state::running; + } + } + + /** + * @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 noexcept { + return current == state::running || current == state::paused; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + [[nodiscard]] bool finished() const 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 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 noexcept { + return current == state::rejected; + } + + /** + * @brief Assigns a child process to run in case of success. + * @tparam Type Type of child process to create. + * @tparam Args Types of arguments to use to initialize the child process. + * @param args Parameters to use to initialize the child process. + * @return A reference to the newly created child process. + */ + template + basic_process &then(Args &&...args) { + const auto &allocator = next.second(); + return *(next.first() = std::allocate_shared(allocator, allocator, std::forward(args)...)); + } + + /** + * @brief Assigns a child process to run in case of success. + * @tparam Func Type of child process to create. + * @param func Either a lambda or a functor to use as a child process. + * @return A reference to the newly created child process. + */ + template + basic_process &then(Func func) { + const auto &allocator = next.second(); + using process_type = internal::process_adaptor; + return *(next.first() = std::allocate_shared(allocator, allocator, std::move(func))); + } + + /** + * @brief Returns the child process without releasing ownership, if any. + * @return The child process attached to the object, if any. + */ + handle_type peek() { + return next.first(); + } + + /** + * @brief Updates a process and its internal state, if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch(current) { + case state::idle: + case state::running: + current = state::running; + update(delta, data); + break; + default: + // suppress warnings + break; + } + + // if it's dead, it must be notified and removed immediately + switch(current) { + case state::succeeded: + succeeded(); + current = state::finished; + break; + case state::failed: + failed(); + current = state::rejected; + break; + case state::aborted: + aborted(); + current = state::rejected; + break; + default: + // suppress warnings + break; + } + } + +private: + compressed_pair next; + state current; +}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct process_adaptor: public basic_process { + using allocator_type = Allocator; + using base_type = basic_process; + using delta_type = typename base_type::delta_type; + + process_adaptor(const allocator_type &allocator, Func proc) + : base_type{allocator}, + func{std::move(proc)} {} + + void update(const delta_type delta, void *data) override { + func(*this, delta, data); + } + +private: + Func func; +}; + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "process/scheduler.hpp" +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/compressed_pair.hpp" + +// #include "fwd.hpp" + +// #include "process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include +#include +#include +#include +// #include "../core/compressed_pair.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct process_adaptor; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Base class for processes. + * + * Derived classes must specify what's the intended type for elapsed times.
+ * A process can implement the following member functions whether required: + * + * * @code{.cpp} + * void update(Delta, void *) override; + * @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 succeeded() override; + * @endcode + * + * It's invoked in case of success, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void failed() override; + * @endcode + * + * It's invoked in case of errors, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void aborted() override; + * @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` member functions and even pause or unpause the process + * itself. + * + * @sa scheduler + * + * @tparam Delta Type to use to provide elapsed time. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_process: public std::enable_shared_from_this> { + enum class state : std::uint8_t { + idle = 0, + running, + paused, + succeeded, + failed, + aborted, + finished, + rejected + }; + + virtual void update(const Delta, void *) { + abort(); + } + + virtual void succeeded() {} + virtual void failed() {} + virtual void aborted() {} + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + + /*! @brief Default constructor. */ + basic_process() + : basic_process{allocator_type{}} {} + + /** + * @brief Constructs a scheduler with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_process(const allocator_type &allocator) + : next{nullptr, allocator}, + current{state::idle} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_process(const basic_process &) = delete; + + /*! @brief Default move constructor, deleted on purpose. */ + basic_process(basic_process &&) = delete; + + /*! @brief Default destructor. */ + virtual ~basic_process() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This process scheduler. + */ + basic_process &operator=(const basic_process &) = delete; + + /** + * @brief Default move assignment operator, deleted on purpose. + * @return This process scheduler. + */ + basic_process &operator=(basic_process &&) = delete; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return next.second(); + } + + /*! @brief Aborts a process if it's still alive, otherwise does nothing. */ + void abort() { + if(alive()) { + current = state::aborted; + } + } + + /** + * @brief Terminates a process with success if it's still alive, otherwise + * does nothing. + */ + void succeed() noexcept { + if(alive()) { + current = state::succeeded; + } + } + + /** + * @brief Terminates a process with errors if it's still alive, otherwise + * does nothing. + */ + void fail() noexcept { + if(alive()) { + current = state::failed; + } + } + + /*! @brief Stops a process if it's running, otherwise does nothing. */ + void pause() noexcept { + if(alive()) { + current = state::paused; + } + } + + /*! @brief Restarts a process if it's paused, otherwise does nothing. */ + void unpause() noexcept { + if(alive()) { + current = state::running; + } + } + + /** + * @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 noexcept { + return current == state::running || current == state::paused; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + [[nodiscard]] bool finished() const 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 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 noexcept { + return current == state::rejected; + } + + /** + * @brief Assigns a child process to run in case of success. + * @tparam Type Type of child process to create. + * @tparam Args Types of arguments to use to initialize the child process. + * @param args Parameters to use to initialize the child process. + * @return A reference to the newly created child process. + */ + template + basic_process &then(Args &&...args) { + const auto &allocator = next.second(); + return *(next.first() = std::allocate_shared(allocator, allocator, std::forward(args)...)); + } + + /** + * @brief Assigns a child process to run in case of success. + * @tparam Func Type of child process to create. + * @param func Either a lambda or a functor to use as a child process. + * @return A reference to the newly created child process. + */ + template + basic_process &then(Func func) { + const auto &allocator = next.second(); + using process_type = internal::process_adaptor; + return *(next.first() = std::allocate_shared(allocator, allocator, std::move(func))); + } + + /** + * @brief Returns the child process without releasing ownership, if any. + * @return The child process attached to the object, if any. + */ + handle_type peek() { + return next.first(); + } + + /** + * @brief Updates a process and its internal state, if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch(current) { + case state::idle: + case state::running: + current = state::running; + update(delta, data); + break; + default: + // suppress warnings + break; + } + + // if it's dead, it must be notified and removed immediately + switch(current) { + case state::succeeded: + succeeded(); + current = state::finished; + break; + case state::failed: + failed(); + current = state::rejected; + break; + case state::aborted: + aborted(); + current = state::rejected; + break; + default: + // suppress warnings + break; + } + } + +private: + compressed_pair next; + state current; +}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct process_adaptor: public basic_process { + using allocator_type = Allocator; + using base_type = basic_process; + using delta_type = typename base_type::delta_type; + + process_adaptor(const allocator_type &allocator, Func proc) + : base_type{allocator}, + func{std::move(proc)} {} + + void update(const delta_type delta, void *data) override { + func(*this, delta, data); + } + +private: + Func func; +}; + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Cooperative scheduler for processes. + * + * A cooperative scheduler runs processes and helps managing their life cycles. + * + * Each process is invoked once per tick. If a process terminates, it's + * removed automatically from the scheduler and it's never invoked again.
+ * A process can also have a child. In this case, the process is replaced with + * its child when it terminates if it returns with success. In case of errors, + * both the process and its child are discarded. + * + * In order to invoke all scheduled processes, call the `update` member function + * passing it the elapsed time to forward to the tasks. + * + * @sa process + * + * @tparam Delta Type to use to provide elapsed time. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_scheduler { + using base_type = basic_process; + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = std::vector, container_allocator>; + +public: + /*! @brief Process type. */ + using type = base_type; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Unsigned integer type. */ + using delta_type = Delta; + + /*! @brief Default constructor. */ + basic_scheduler() + : basic_scheduler{allocator_type{}} {} + + /** + * @brief Constructs a scheduler with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_scheduler(const allocator_type &allocator) + : handlers{allocator, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_scheduler(const basic_scheduler &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_scheduler(basic_scheduler &&other) noexcept + : handlers{std::move(other.handlers)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a scheduler is not allowed"); + } + + /*! @brief Default destructor. */ + ~basic_scheduler() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This process scheduler. + */ + basic_scheduler &operator=(const basic_scheduler &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This process scheduler. + */ + basic_scheduler &operator=(basic_scheduler &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a scheduler is not allowed"); + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given scheduler. + * @param other Scheduler to exchange the content with. + */ + void swap(basic_scheduler &other) noexcept { + using std::swap; + swap(handlers, other.handlers); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return handlers.second(); + } + + /** + * @brief Number of processes currently scheduled. + * @return Number of processes currently scheduled. + */ + [[nodiscard]] size_type size() const noexcept { + return handlers.first().size(); + } + + /** + * @brief Returns true if at least a process is currently scheduled. + * @return True if there are scheduled processes, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return handlers.first().empty(); + } + + /** + * @brief Discards all scheduled processes. + * + * Processes aren't aborted. They are discarded along with their children + * and never executed again. + */ + void clear() { + handlers.first().clear(); + } + + /** + * @brief Schedules a process for the next tick. + * @tparam Type Type of process to create. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return A reference to the newly created process. + */ + template + type &attach(Args &&...args) { + const auto &allocator = handlers.second(); + return *handlers.first().emplace_back(std::allocate_shared(allocator, allocator, std::forward(args)...)); + } + + /** + * @brief Schedules a process for the next tick. + * @tparam Func Type of process to create. + * @param func Either a lambda or a functor to use as a process. + * @return A reference to the newly created process. + */ + template + type &attach(Func func) { + const auto &allocator = handlers.second(); + using process_type = internal::process_adaptor; + return *handlers.first().emplace_back(std::allocate_shared(allocator, allocator, std::move(func))); + } + + /** + * @brief Updates all scheduled processes. + * + * All scheduled processes are executed in no specific order.
+ * If a process terminates with success, it's replaced with its child, if + * any. Otherwise, if a process terminates with an error, it's removed along + * with its child. + * + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const delta_type delta, void *data = nullptr) { + for(auto next = handlers.first().size(); next; --next) { + const auto pos = next - 1u; + handlers.first()[pos]->tick(delta, data); + // updating might spawn/reallocate, cannot hold refs until here + auto &elem = handlers.first()[pos]; + + if(elem->finished()) { + elem = elem->peek(); + } + + if(!elem || elem->rejected()) { + elem = std::move(handlers.first().back()); + handlers.first().pop_back(); + } + } + } + + /** + * @brief Aborts all scheduled processes. + * + * Unless an immediate operation is requested, the abort is scheduled for + * the next tick. Processes won't be executed anymore in any case.
+ * Once a process is fully aborted and thus finished, it's discarded along + * with its child, if any. + * + * @param immediate Requests an immediate operation. + */ + void abort(const bool immediate = false) { + for(auto &&curr: handlers.first()) { + curr->abort(); + + if(immediate) { + curr->tick({}); + } + } + } + +private: + compressed_pair handlers; +}; + +} // namespace entt + +#endif + +// #include "resource/cache.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP +#define ENTT_RESOURCE_RESOURCE_CACHE_HPP + +#include +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator> +class dense_set; + +template +class basic_table; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Element types. + */ +template +using table = basic_table...>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits::max)(); + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + const dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + const dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept = default; + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return (offset = it[static_cast(offset)].next), *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + const dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + const auto idx = static_cast(offset); + return {it[idx].element.first, it[idx].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_map_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_map() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses a given element with bounds checking. + * @tparam Other Type of the key of an element to find. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_RESOURCE_FWD_HPP +#define ENTT_RESOURCE_FWD_HPP + +#include + +namespace entt { + +template +struct resource_loader; + +template, typename = std::allocator> +class resource_cache; + +template +class resource; + +} // namespace entt + +#endif + +// #include "loader.hpp" +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Transparent loader for shared resources. + * @tparam Type Type of resources created by the loader. + */ +template +struct resource_loader { + /*! @brief Result type. */ + using result_type = std::shared_ptr; + + /** + * @brief Constructs a shared pointer to a resource from its arguments. + * @tparam Args Types of arguments to use to construct the resource. + * @param args Parameters to use to construct the resource. + * @return A shared pointer to a resource of the given type. + */ + template + result_type operator()(Args &&...args) const { + return std::make_shared(std::forward(args)...); + } +}; + +} // namespace entt + +#endif + +// #include "resource.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Basic resource handle. + * + * A handle wraps a resource and extends its lifetime. It also shares the same + * resource with all other handles constructed from the same element.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to push references around. + * + * @tparam Type Type of resource managed by a handle. + */ +template +class resource { + template + friend class resource; + + template + static constexpr bool is_acceptable = !std::is_same_v && std::is_constructible_v; + +public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + + /*! @brief Default constructor. */ + resource() noexcept + : value{} {} + + /** + * @brief Creates a new resource handle. + * @param res A handle to a resource. + */ + explicit resource(handle_type res) noexcept + : value{std::move(res)} {} + + /*! @brief Default copy constructor. */ + resource(const resource &) noexcept = default; + + /*! @brief Default move constructor. */ + resource(resource &&) noexcept = default; + + /** + * @brief Aliasing constructor. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle with which to share ownership information. + * @param res Unrelated and unmanaged resources. + */ + template + resource(const resource &other, element_type &res) noexcept + : value{other.value, std::addressof(res)} {} + + /** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ + template>> + resource(const resource &other) noexcept + : value{other.value} {} + + /** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ + template>> + resource(resource &&other) noexcept + : value{std::move(other.value)} {} + + /*! @brief Default destructor. */ + ~resource() = default; + + /** + * @brief Default copy assignment operator. + * @return This resource handle. + */ + resource &operator=(const resource &) noexcept = default; + + /** + * @brief Default move assignment operator. + * @return This resource handle. + */ + resource &operator=(resource &&) noexcept = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ + template>> + resource &operator=(const resource &other) noexcept { + value = other.value; + return *this; + } + + /** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + * @return This resource handle. + */ + template>> + resource &operator=(resource &&other) noexcept { + value = std::move(other.value); + return *this; + } + + /** + * @brief Exchanges the content with that of a given resource. + * @param other Resource to exchange the content with. + */ + void swap(resource &other) noexcept { + using std::swap; + swap(value, other.value); + } + + /** + * @brief Returns a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A reference to the managed resource. + */ + [[nodiscard]] element_type &operator*() const noexcept { + return *value; + } + + /*! @copydoc operator* */ + [[nodiscard]] operator element_type &() const noexcept { + return *value; + } + + /** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ + [[nodiscard]] element_type *operator->() const noexcept { + return value.get(); + } + + /** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(value); + } + + /*! @brief Releases the ownership of the managed resource. */ + void reset() { + value.reset(); + } + + /** + * @brief Replaces the managed resource. + * @param other A handle to a resource. + */ + void reset(handle_type other) { + value = std::move(other); + } + + /** + * @brief Returns the underlying resource handle. + * @return The underlying resource handle. + */ + [[nodiscard]] handle_type handle() const noexcept { + return value; + } + +private: + handle_type value; +}; + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same resource, false otherwise. + */ +template +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) == std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same resource, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than the second, false otherwise. + */ +template +[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) < std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than the second, false otherwise. + */ +template +[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs > rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class resource_cache_iterator final { + template + friend class resource_cache_iterator; + +public: + using value_type = std::pair>; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr resource_cache_iterator() noexcept = default; + + constexpr resource_cache_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr resource_cache_iterator(const resource_cache_iterator, Other> &other) noexcept + : it{other.it} {} + + constexpr resource_cache_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr resource_cache_iterator operator++(int) noexcept { + const resource_cache_iterator orig = *this; + return ++(*this), orig; + } + + constexpr resource_cache_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr resource_cache_iterator operator--(int) noexcept { + const resource_cache_iterator orig = *this; + return operator--(), orig; + } + + constexpr resource_cache_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr resource_cache_iterator operator+(const difference_type value) const noexcept { + resource_cache_iterator copy = *this; + return (copy += value); + } + + constexpr resource_cache_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr resource_cache_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, resource{it[value].second}}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + + template + friend constexpr bool operator==(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + + template + friend constexpr bool operator<(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic cache for resources of any type. + * @tparam Type Type of resources managed by a cache. + * @tparam Loader Type of loader used to create the resources. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class resource_cache { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Resource type. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Loader type. */ + using loader_type = Loader; + /*! @brief Input iterator type. */ + using iterator = internal::resource_cache_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::resource_cache_iterator; + + /*! @brief Default constructor. */ + resource_cache() + : resource_cache{loader_type{}} {} + + /** + * @brief Constructs an empty cache with a given allocator. + * @param allocator The allocator to use. + */ + explicit resource_cache(const allocator_type &allocator) + : resource_cache{loader_type{}, allocator} {} + + /** + * @brief Constructs an empty cache with a given allocator and loader. + * @param callable The loader to use. + * @param allocator The allocator to use. + */ + explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{}) + : pool{container_type{allocator}, callable} {} + + /*! @brief Default copy constructor. */ + resource_cache(const resource_cache &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + resource_cache(const resource_cache &other, const allocator_type &allocator) + : pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {} + + /*! @brief Default move constructor. */ + resource_cache(resource_cache &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + resource_cache(resource_cache &&other, const allocator_type &allocator) + : pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {} + + /*! @brief Default destructor. */ + ~resource_cache() = default; + + /** + * @brief Default copy assignment operator. + * @return This cache. + */ + resource_cache &operator=(const resource_cache &) = default; + + /** + * @brief Default move assignment operator. + * @return This cache. + */ + resource_cache &operator=(resource_cache &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pool.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the cache is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal cache. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return pool.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return pool.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal cache. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return pool.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return pool.first().end(); + } + + /** + * @brief Returns true if a cache contains no resources, false otherwise. + * @return True if the cache contains no resources, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return pool.first().empty(); + } + + /** + * @brief Number of resources managed by a cache. + * @return Number of resources currently stored. + */ + [[nodiscard]] size_type size() const noexcept { + return pool.first().size(); + } + + /*! @brief Clears a cache. */ + void clear() noexcept { + pool.first().clear(); + } + + /** + * @brief Loads a resource, if its identifier does not exist. + * + * Arguments are forwarded directly to the loader and _consumed_ only if the + * resource doesn't already exist. + * + * @warning + * If the resource isn't loaded correctly, the returned handle could be + * invalid and any use of it will result in undefined behavior. + * + * @tparam Args Types of arguments to use to load the resource if required. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource if required. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair load(const id_type id, Args &&...args) { + if(auto it = pool.first().find(id); it != pool.first().end()) { + return {it, false}; + } + + return pool.first().emplace(id, pool.second()(std::forward(args)...)); + } + + /** + * @brief Force loads a resource, if its identifier does not exist. + * @copydetails load + */ + template + std::pair force_load(const id_type id, Args &&...args) { + return {pool.first().insert_or_assign(id, pool.second()(std::forward(args)...)).first, true}; + } + + /** + * @brief Returns a handle for a given resource identifier. + * + * @warning + * There is no guarantee that the returned handle is valid.
+ * If it is not, any use will result in indefinite behavior. + * + * @param id Unique resource identifier. + * @return A handle for the given resource. + */ + [[nodiscard]] resource operator[](const id_type id) const { + if(auto it = pool.first().find(id); it != pool.first().cend()) { + return resource{it->second}; + } + + return {}; + } + + /*! @copydoc operator[] */ + [[nodiscard]] resource operator[](const id_type id) { + if(auto it = pool.first().find(id); it != pool.first().end()) { + return resource{it->second}; + } + + return {}; + } + + /** + * @brief Checks if a cache contains a given identifier. + * @param id Unique resource identifier. + * @return True if the cache contains the resource, false otherwise. + */ + [[nodiscard]] bool contains(const id_type id) const { + return pool.first().contains(id); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto it = pool.first().begin(); + return pool.first().erase(it + (pos - const_iterator{it})); + } + + /** + * @brief Removes the given elements from a cache. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto it = pool.first().begin(); + return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it})); + } + + /** + * @brief Removes the given elements from a cache. + * @param id Unique resource identifier. + * @return Number of resources erased (either 0 or 1). + */ + size_type erase(const id_type id) { + return pool.first().erase(id); + } + + /** + * @brief Returns the loader used to create resources. + * @return The loader used to create resources. + */ + [[nodiscard]] loader_type loader() const { + return pool.second(); + } + +private: + compressed_pair pool; +}; + +} // namespace entt + +#endif + +// #include "resource/loader.hpp" +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Transparent loader for shared resources. + * @tparam Type Type of resources created by the loader. + */ +template +struct resource_loader { + /*! @brief Result type. */ + using result_type = std::shared_ptr; + + /** + * @brief Constructs a shared pointer to a resource from its arguments. + * @tparam Args Types of arguments to use to construct the resource. + * @param args Parameters to use to construct the resource. + * @return A shared pointer to a resource of the given type. + */ + template + result_type operator()(Args &&...args) const { + return std::make_shared(std::forward(args)...); + } +}; + +} // namespace entt + +#endif + +// #include "resource/resource.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Basic resource handle. + * + * A handle wraps a resource and extends its lifetime. It also shares the same + * resource with all other handles constructed from the same element.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to push references around. + * + * @tparam Type Type of resource managed by a handle. + */ +template +class resource { + template + friend class resource; + + template + static constexpr bool is_acceptable = !std::is_same_v && std::is_constructible_v; + +public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + + /*! @brief Default constructor. */ + resource() noexcept + : value{} {} + + /** + * @brief Creates a new resource handle. + * @param res A handle to a resource. + */ + explicit resource(handle_type res) noexcept + : value{std::move(res)} {} + + /*! @brief Default copy constructor. */ + resource(const resource &) noexcept = default; + + /*! @brief Default move constructor. */ + resource(resource &&) noexcept = default; + + /** + * @brief Aliasing constructor. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle with which to share ownership information. + * @param res Unrelated and unmanaged resources. + */ + template + resource(const resource &other, element_type &res) noexcept + : value{other.value, std::addressof(res)} {} + + /** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ + template>> + resource(const resource &other) noexcept + : value{other.value} {} + + /** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ + template>> + resource(resource &&other) noexcept + : value{std::move(other.value)} {} + + /*! @brief Default destructor. */ + ~resource() = default; + + /** + * @brief Default copy assignment operator. + * @return This resource handle. + */ + resource &operator=(const resource &) noexcept = default; + + /** + * @brief Default move assignment operator. + * @return This resource handle. + */ + resource &operator=(resource &&) noexcept = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ + template>> + resource &operator=(const resource &other) noexcept { + value = other.value; + return *this; + } + + /** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + * @return This resource handle. + */ + template>> + resource &operator=(resource &&other) noexcept { + value = std::move(other.value); + return *this; + } + + /** + * @brief Exchanges the content with that of a given resource. + * @param other Resource to exchange the content with. + */ + void swap(resource &other) noexcept { + using std::swap; + swap(value, other.value); + } + + /** + * @brief Returns a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A reference to the managed resource. + */ + [[nodiscard]] element_type &operator*() const noexcept { + return *value; + } + + /*! @copydoc operator* */ + [[nodiscard]] operator element_type &() const noexcept { + return *value; + } + + /** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ + [[nodiscard]] element_type *operator->() const noexcept { + return value.get(); + } + + /** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(value); + } + + /*! @brief Releases the ownership of the managed resource. */ + void reset() { + value.reset(); + } + + /** + * @brief Replaces the managed resource. + * @param other A handle to a resource. + */ + void reset(handle_type other) { + value = std::move(other); + } + + /** + * @brief Returns the underlying resource handle. + * @return The underlying resource handle. + */ + [[nodiscard]] handle_type handle() const noexcept { + return value; + } + +private: + handle_type value; +}; + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same resource, false otherwise. + */ +template +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) == std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same resource, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than the second, false otherwise. + */ +template +[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) < std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than the second, false otherwise. + */ +template +[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs > rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace entt + +#endif + +// #include "signal/delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include + +namespace entt { + +template +class delegate; + +template> +class basic_dispatcher; + +template> +class emitter; + +class connection; + +struct scoped_connection; + +template +class sink; + +template> +class sigh; + +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; + +/*! @brief Disambiguation tag for constructors and the like. */ +template +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; + +/** + * @brief Constant of type connect_arg_t used to disambiguate calls. + * @tparam Candidate Element to connect (likely a free or member function). + */ +template +inline constexpr connect_arg_t connect_arg{}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template>> +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 { + using return_type = std::remove_const_t; + using delegate_type = return_type(const void *, Args...); + + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> return_type { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + 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() noexcept = default; + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) 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) 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() noexcept { + instance = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> return_type { + 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) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> return_type { + 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) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> return_type { + 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) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); + return fn(instance, 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 noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance{}; + delegate_type *fn{}; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) 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; + +} // namespace entt + +#endif + +// #include "signal/dispatcher.hpp" +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + +// #include "../core/bit.hpp" +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 16 +#define ENTT_VERSION_PATCH 0 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +// NOLINTEND(cppcoreguidelines-macro-*,modernize-macro-*) + +#endif + + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if __has_include() +# include +# +# if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +# endif +#endif + +#ifndef ENTT_CONSTEVAL +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(((condition) && (msg))) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_NO_MIXIN +# define ENTT_STORAGE(Mixin, ...) __VA_ARGS__ +#else +# define ENTT_STORAGE(Mixin, ...) Mixin<__VA_ARGS__> +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +// NOLINTEND(cppcoreguidelines-macro-usage) + +#endif + + +namespace entt { + +/** + * @brief Returns the number of set bits in a value (waiting for C++20 and + * `std::popcount`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The number of set bits in the value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, int> popcount(const Type value) noexcept { + return value ? (int(value & 1) + popcount(static_cast(value >> 1))) : 0; +} + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return True if the value is a power of two, false otherwise. + */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> has_single_bit(const Type value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @return The smallest power of two greater than or equal to the given value. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> next_power_of_two(const Type value) noexcept { + // NOLINTNEXTLINE(bugprone-assert-side-effect) + ENTT_ASSERT_CONSTEXPR(value < (Type{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + Type curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= (curr >> next); + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @tparam Type Unsigned integer type. + * @param value A value of unsigned integer type. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> fast_mod(const Type value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + const iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v && std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return ::new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#include +#include +#include + +namespace entt { + +template< + typename Key, + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator>> +class dense_map; + +template< + typename Type, + typename = std::hash, + typename = std::equal_to<>, + typename = std::allocator> +class dense_set; + +template +class basic_table; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Element types. + */ +template +using table = basic_table...>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +static constexpr std::size_t dense_map_placeholder_position = (std::numeric_limits::max)(); + +template +struct dense_map_node final { + using value_type = std::pair; + + template + dense_map_node(const std::size_t pos, Args &&...args) + : next{pos}, + element{std::forward(args)...} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + template + dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} + + std::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + const dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + const dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept = default; + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return (offset = it[static_cast(offset)].next), *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + const dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + const auto idx = static_cast(offset); + return {it[idx].element.first, it[idx].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + std::size_t offset{dense_map_placeholder_position}; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for key-value pairs with unique keys. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on the hash of its key. Keys with the same hash + * code appear in the same bucket. + * + * @tparam Key Key type of the associative container. + * @tparam Type Mapped type of the associative container. + * @tparam Hash Type of function to use to hash the keys. + * @tparam KeyEqual Type of function to use to compare the keys for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_map { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + static constexpr std::size_t placeholder_position = internal::dense_map_placeholder_position; + + using node_type = internal::dense_map_node; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return begin() + static_cast(offset); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, const std::size_t bucket) const { + for(auto offset = sparse.first()[bucket]; offset != placeholder_position; offset = packed.first()[offset].next) { + if(packed.second()(packed.first()[offset].element.first, key)) { + return cbegin() + static_cast(offset); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = &sparse.first()[key_to_bucket(packed.first().back().element.first)]; + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(const auto bc = bucket_count(); size() > static_cast(static_cast(bc) * max_load_factor())) { + rehash(bc * 2u); + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Signed integer type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /*! @brief Default destructor. */ + ~dense_map() = default; + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept = default; + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) noexcept { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[static_cast(from) - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = &sparse.first()[key_to_bucket(key)]; *curr != placeholder_position; curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses a given element with bounds checking. + * @tparam Other Type of the key of an element to find. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + at(const Other &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return static_cast(size()) / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(static_cast(size()) / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = placeholder_position; + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(static_cast(cnt) / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold{default_threshold}; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "fwd.hpp" + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t 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_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = 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 First 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 First 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 = First; +}; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool 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 Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + // NOLINTNEXTLINE(modernize-type-traits) + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::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 type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +struct value_list_diff, value_list> { + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, Value>, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::type; + +/*! @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 bool 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 bool 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 The type to test. + */ +template +struct is_complete: std::false_type {}; + +/*! @copydoc is_complete */ +template +struct is_complete>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_complete_v = is_complete::value; + +/** + * @brief Provides the member constant `value` to true if a given type is an + * iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: std::false_type {}; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : internal::has_iterator_category {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_iterator_v = is_iterator::value; + +/** + * @brief Provides the member constant `value` to true if a given type is both + * an empty and non-final class, false otherwise. + * @tparam Type The type to test + */ +template +struct is_ebco_eligible + : std::bool_constant && !std::is_final_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; + +/** + * @brief Provides the member constant `value` to true if `Type::is_transparent` + * is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: std::false_type {}; + +/*! @copydoc is_transparent */ +template +struct is_transparent>: std::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || std::is_same_v || dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else { + return maybe_equality_comparable(0); + } + // NOLINTEND(modernize-use-transparent-functors) +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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 = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +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; + +/** + * @brief Extracts the n-th argument of a _callable_ type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid _callable_ type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + + template + static constexpr decltype(pick_up(&Type::operator())) pick_up(Type &&); + +public: + /*! @brief N-th argument of the _callable_ type. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v && std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v && std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /*! @brief Default destructor. */ + ~compressed_pair() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) noexcept { + lhs.swap(rhs); +} + +} // namespace entt + +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, no element available. */ + empty, + /*! @brief Owning mode, dynamically allocated element. */ + dynamic, + /*! @brief Owning mode, embedded element. */ + embedded, + /*! @brief Aliasing mode, non-const reference. */ + ref, + /*! @brief Const aliasing mode, const reference. */ + cref +}; + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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<>; + +template +class compressed_pair; + +template +class basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +// NOLINTNEXTLINE(bugprone-forward-declaration-namespace) +struct type_info; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct fnv_1a_params; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 2166136261; + static constexpr auto prime = 16777619; +}; + +template<> +struct fnv_1a_params { + static constexpr auto offset = 14695981039346656037ull; + static constexpr auto prime = 1099511628211ull; +}; + +template +struct basic_hashed_string { + using value_type = Char; + using size_type = std::size_t; + using hash_type = id_type; + + const value_type *repr{}; + hash_type hash{fnv_1a_params<>::offset}; + size_type length{}; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Zero overhead unique identifier. + * + * A hashed string is a compile-time tool that allows users to use + * human-readable identifiers in the codebase while using their numeric + * counterparts at runtime.
+ * Because of that, a hashed string can also be used in constant expressions if + * required. + * + * @warning + * This class doesn't take ownership of user-supplied strings nor does it make a + * copy of them. + * + * @tparam Char Character type. + */ +template +class basic_hashed_string: internal::basic_hashed_string { + using base_type = internal::basic_hashed_string; + using params = internal::fnv_1a_params<>; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const typename base_type::value_type *str) noexcept + : repr{str} {} + + const typename base_type::value_type *repr; + }; + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + [[nodiscard]] static ENTT_CONSTEVAL hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : basic_hashed_string{nullptr, 0u} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; base_type::length < len; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) + ENTT_CONSTEVAL basic_hashed_string(const value_type (&str)[N]) noexcept + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + : base_type{str} { + for(; str[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(str[base_type::length])) * params::prime; + } + } + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{wrapper.repr} { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; wrapper.repr[base_type::length]; ++base_type::length) { + base_type::hash = (base_type::hash ^ static_cast(wrapper.repr[base_type::length])) * params::prime; + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + } + + /** + * @brief Returns the size of a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] explicit constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays) +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) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_string operator""_hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] ENTT_CONSTEVAL hashed_wstring operator""_hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr const char *pretty_function() noexcept { +#if defined ENTT_PRETTY_FUNCTION + return static_cast(ENTT_PRETTY_FUNCTION); +#else + return ""; +#endif +} + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + const std::string_view full_name{pretty_function()}; + auto first = full_name.find_first_not_of(' ', full_name.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = full_name.substr(first, full_name.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const 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() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + // NOLINTBEGIN(modernize-use-transparent-functors) + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + // NOLINTEND(modernize-use-transparent-functors) + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static const type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +// NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const 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) 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) 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; + +/** + * @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. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : 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 + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template>> +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 { + using return_type = std::remove_const_t; + using delegate_type = return_type(const void *, Args...); + + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> return_type { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + [[maybe_unused]] constexpr auto offset = !std::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + 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() noexcept = default; + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) 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) 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() noexcept { + instance = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> return_type { + 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) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> return_type { + 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) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> return_type { + 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) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); + return fn(instance, 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 noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance{}; + delegate_type *fn{}; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) 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; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink>; + + /*! @brief Default constructor. */ + sigh() noexcept(noexcept(allocator_type{})) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) + : calls{std::move(other.calls), allocator} {} + + /*! @brief Default destructor. */ + ~sigh() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept { + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const 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 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 pos = calls.size(); pos; --pos) { + calls[pos - 1u](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 pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](args...)); + } + } + } + } + +private: + container_type calls; +}; + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const 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 Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection &operator=(const scoped_connection &) = delete; + + /** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection &operator=(connection other) { + conn = 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 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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + using difference_type = typename signal_type::container_type::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(); + } + + template + void disconnect_if(Func callback) { + auto &ref = signal_or_assert(); + + for(auto pos = ref.calls.size(); pos; --pos) { + if(auto &elem = ref.calls[pos - 1u]; callback(elem)) { + elem = std::move(ref.calls.back()); + ref.calls.pop_back(); + } + } + } + + [[nodiscard]] auto &signal_or_assert() const noexcept { + ENTT_ASSERT(signal != nullptr, "Invalid pointer to signal"); + return *signal; + } + +public: + /*! @brief Constructs an invalid sink. */ + sink() noexcept + : signal{} {} + + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) noexcept + : 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 noexcept { + return signal_or_assert().calls.empty(); + } + + /** + * @brief Connects a free function or an unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate_type call{}; + call.template connect(); + signal_or_assert().calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return {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.
+ * 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 reference that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &value_or_instance) { + disconnect(value_or_instance); + + delegate_type call{}; + call.template connect(value_or_instance); + signal_or_assert().calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return {conn, signal}; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * @sa connect(Type &) + * + * @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 pointer that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type *value_or_instance) { + disconnect(value_or_instance); + + delegate_type call{}; + call.template connect(value_or_instance); + signal_or_assert().calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return {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() { + delegate_type call{}; + call.template connect(); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects a free function with payload or a bound member from 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.
+ * 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 disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * + * @sa disconnect(Type &) + * + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + ENTT_ASSERT(value_or_instance != nullptr, "Invalid value or instance"); + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal_or_assert().calls.clear(); + } + + /** + * @brief Returns true if a sink is correctly initialized, false otherwise. + * @return True if a sink is correctly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return signal != nullptr; + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler type of a sink directly from the + * signal it refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +sink(sigh &) -> sink>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct basic_dispatcher_handler { + virtual ~basic_dispatcher_handler() = default; + virtual void publish() = 0; + virtual void disconnect(void *) = 0; + virtual void clear() noexcept = 0; + [[nodiscard]] virtual std::size_t size() const noexcept = 0; +}; + +template +class dispatcher_handler final: public basic_dispatcher_handler { + static_assert(std::is_same_v>, "Invalid type"); + + using alloc_traits = std::allocator_traits; + using signal_type = sigh; + using container_type = std::vector>; + +public: + using allocator_type = Allocator; + + dispatcher_handler(const allocator_type &allocator) + : signal{allocator}, + events{allocator} {} + + void publish() override { + const auto length = events.size(); + + for(std::size_t pos{}; pos < length; ++pos) { + signal.publish(events[pos]); + } + + events.erase(events.cbegin(), events.cbegin() + static_cast(length)); + } + + void disconnect(void *instance) override { + bucket().disconnect(instance); + } + + void clear() noexcept override { + events.clear(); + } + + [[nodiscard]] auto bucket() noexcept { + return typename signal_type::sink_type{signal}; + } + + void trigger(Type event) { + signal.publish(event); + } + + template + void enqueue(Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + events.push_back(Type{std::forward(args)...}); + } else { + events.emplace_back(std::forward(args)...); + } + } + + [[nodiscard]] std::size_t size() const noexcept override { + return events.size(); + } + +private: + signal_type signal; + container_type events; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic dispatcher implementation. + * + * A dispatcher can be used either to trigger an immediate event or to enqueue + * events to be published all together once per tick.
+ * Listeners are provided in the form of member functions. For each event of + * type `Type`, listeners are such that they can be invoked with an argument of + * type `Type &`, no matter what the return type is. + * + * The dispatcher creates instances of the `sigh` class internally. Refer to the + * documentation of the latter for more details. + * + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_dispatcher { + template + using handler_type = internal::dispatcher_handler; + + using key_type = id_type; + // std::shared_ptr because of its type erased allocator which is useful here + using mapped_type = std::shared_ptr; + + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + + template + [[nodiscard]] handler_type &assure(const id_type id) { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + auto &&ptr = pools.first()[id]; + + if(!ptr) { + const auto &allocator = get_allocator(); + ptr = std::allocate_shared>(allocator, allocator); + } + + return static_cast &>(*ptr); + } + + template + [[nodiscard]] const handler_type *assure(const id_type id) const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + + if(auto it = pools.first().find(id); it != pools.first().cend()) { + return static_cast *>(it->second.get()); + } + + return nullptr; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + basic_dispatcher() + : basic_dispatcher{allocator_type{}} {} + + /** + * @brief Constructs a dispatcher with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_dispatcher(const allocator_type &allocator) + : pools{allocator, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_dispatcher(const basic_dispatcher &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_dispatcher(basic_dispatcher &&other) noexcept + : pools{std::move(other.pools)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) + : pools{container_type{std::move(other.pools.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed"); + } + + /*! @brief Default destructor. */ + ~basic_dispatcher() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This dispatcher. + */ + basic_dispatcher &operator=(const basic_dispatcher &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This dispatcher. + */ + basic_dispatcher &operator=(basic_dispatcher &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || get_allocator() == other.get_allocator(), "Copying a dispatcher is not allowed"); + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given dispatcher. + * @param other Dispatcher to exchange the content with. + */ + void swap(basic_dispatcher &other) noexcept { + using std::swap; + swap(pools, other.pools); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pools.second(); + } + + /** + * @brief Returns the number of pending events for a given type. + * @tparam Type Type of event for which to return the count. + * @param id Name used to map the event queue within the dispatcher. + * @return The number of pending events for the given type. + */ + template + [[nodiscard]] size_type size(const id_type id = type_hash::value()) const noexcept { + const auto *cpool = assure>(id); + return cpool ? cpool->size() : 0u; + } + + /** + * @brief Returns the total number of pending events. + * @return The total number of pending events. + */ + [[nodiscard]] size_type size() const noexcept { + size_type count{}; + + for(auto &&cpool: pools.first()) { + count += cpool.second->size(); + } + + return count; + } + + /** + * @brief Returns a sink object for the given event and queue. + * + * A sink is an opaque object used to connect listeners to events. + * + * The function type for a listener is _compatible_ with: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * The order of invocation of the listeners isn't guaranteed. + * + * @sa sink + * + * @tparam Type Type of event of which to get the sink. + * @param id Name used to map the event queue within the dispatcher. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto sink(const id_type id = type_hash::value()) { + return assure(id).bucket(); + } + + /** + * @brief Triggers an immediate event of a given type. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + */ + template + void trigger(Type &&value = {}) { + trigger(type_hash>::value(), std::forward(value)); + } + + /** + * @brief Triggers an immediate event on a queue of a given type. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + * @param id Name used to map the event queue within the dispatcher. + */ + template + void trigger(const id_type id, Type &&value = {}) { + assure>(id).trigger(std::forward(value)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template + void enqueue(Args &&...args) { + enqueue_hint(type_hash::value(), std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @param value An instance of the given type of event. + */ + template + void enqueue(Type &&value) { + enqueue_hint(type_hash>::value(), std::forward(value)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param id Name used to map the event queue within the dispatcher. + * @param args Arguments to use to construct the event. + */ + template + void enqueue_hint(const id_type id, Args &&...args) { + assure(id).enqueue(std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @param id Name used to map the event queue within the dispatcher. + * @param value An instance of the given type of event. + */ + template + void enqueue_hint(const id_type id, Type &&value) { + assure>(id).enqueue(std::forward(value)); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + for(auto &&cpool: pools.first()) { + cpool.second->disconnect(value_or_instance); + } + } + + /** + * @brief Discards all the events stored so far in a given queue. + * @tparam Type Type of event to discard. + * @param id Name used to map the event queue within the dispatcher. + */ + template + void clear(const id_type id = type_hash::value()) { + assure(id).clear(); + } + + /*! @brief Discards all the events queued so far. */ + void clear() noexcept { + for(auto &&cpool: pools.first()) { + cpool.second->clear(); + } + } + + /** + * @brief Delivers all the pending events of a given queue. + * @tparam Type Type of event to send. + * @param id Name used to map the event queue within the dispatcher. + */ + template + void update(const id_type id = type_hash::value()) { + assure(id).publish(); + } + + /*! @brief Delivers all the pending events. */ + void update() const { + for(auto &&cpool: pools.first()) { + cpool.second->publish(); + } + } + +private: + compressed_pair pools; +}; + +} // namespace entt + +#endif + +// #include "signal/emitter.hpp" +#ifndef ENTT_SIGNAL_EMITTER_HPP +#define ENTT_SIGNAL_EMITTER_HPP + +#include +#include +#include +// #include "../container/dense_map.hpp" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/utility.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief General purpose event emitter. + * + * To create an emitter type, derived classes must inherit from the base as: + * + * @code{.cpp} + * struct my_emitter: emitter { + * // ... + * } + * @endcode + * + * Handlers for the different events are created internally on the fly. It's not + * required to specify in advance the full list of accepted events.
+ * Moreover, whenever an event is published, an emitter also passes a reference + * to itself to its listeners. + * + * @tparam Derived Emitter type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class emitter { + using key_type = id_type; + using mapped_type = std::function; + + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + emitter() + : emitter{allocator_type{}} {} + + /** + * @brief Constructs an emitter with a given allocator. + * @param allocator The allocator to use. + */ + explicit emitter(const allocator_type &allocator) + : handlers{allocator, allocator} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + emitter(const emitter &) = delete; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + emitter(emitter &&other) noexcept + : handlers{std::move(other.handlers)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + emitter(emitter &&other, const allocator_type &allocator) + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + } + + /*! @brief Default destructor. */ + virtual ~emitter() { + static_assert(std::is_base_of_v, Derived>, "Invalid emitter type"); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This emitter. + */ + emitter &operator=(const emitter &) = delete; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This emitter. + */ + emitter &operator=(emitter &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given emitter. + * @param other Emitter to exchange the content with. + */ + void swap(emitter &other) noexcept { + using std::swap; + swap(handlers, other.handlers); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return handlers.second(); + } + + /** + * @brief Publishes a given event. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + */ + template + void publish(Type value) { + if(const auto id = type_id().hash(); handlers.first().contains(id)) { + handlers.first()[id](&value); + } + } + + /** + * @brief Registers a listener with the event emitter. + * @tparam Type Type of event to which to connect the listener. + * @param func The listener to register. + */ + template + void on(std::function func) { + handlers.first().insert_or_assign(type_id().hash(), [func = std::move(func), this](void *value) { + func(*static_cast(value), static_cast(*this)); + }); + } + + /** + * @brief Disconnects a listener from the event emitter. + * @tparam Type Type of event of the listener. + */ + template + void erase() { + handlers.first().erase(type_hash>>::value()); + } + + /*! @brief Disconnects all the listeners. */ + void clear() noexcept { + handlers.first().clear(); + } + + /** + * @brief Checks if there are listeners registered for the specific event. + * @tparam Type Type of event to test. + * @return True if there are no listeners registered, false otherwise. + */ + template + [[nodiscard]] bool contains() const { + return handlers.first().contains(type_hash>>::value()); + } + + /** + * @brief Checks if there are listeners registered with the event emitter. + * @return True if there are no listeners registered, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return handlers.first().empty(); + } + +private: + compressed_pair handlers; +}; + +} // namespace entt + +#endif + +// #include "signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "delegate.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink>; + + /*! @brief Default constructor. */ + sigh() noexcept(noexcept(allocator_type{})) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) + : calls{std::move(other.calls), allocator} {} + + /*! @brief Default destructor. */ + ~sigh() = default; + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept { + swap(other); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const 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 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 pos = calls.size(); pos; --pos) { + calls[pos - 1u](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 pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](args...)); + } + } + } + } + +private: + container_type calls; +}; + +/** + * @brief Connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it. + */ +class connection { + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const 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 Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection &operator=(const scoped_connection &) = delete; + + /** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection &operator=(connection other) { + conn = 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 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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + using difference_type = typename signal_type::container_type::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(); + } + + template + void disconnect_if(Func callback) { + auto &ref = signal_or_assert(); + + for(auto pos = ref.calls.size(); pos; --pos) { + if(auto &elem = ref.calls[pos - 1u]; callback(elem)) { + elem = std::move(ref.calls.back()); + ref.calls.pop_back(); + } + } + } + + [[nodiscard]] auto &signal_or_assert() const noexcept { + ENTT_ASSERT(signal != nullptr, "Invalid pointer to signal"); + return *signal; + } + +public: + /*! @brief Constructs an invalid sink. */ + sink() noexcept + : signal{} {} + + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) noexcept + : 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 noexcept { + return signal_or_assert().calls.empty(); + } + + /** + * @brief Connects a free function or an unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate_type call{}; + call.template connect(); + signal_or_assert().calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return {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.
+ * 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 reference that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &value_or_instance) { + disconnect(value_or_instance); + + delegate_type call{}; + call.template connect(value_or_instance); + signal_or_assert().calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return {conn, signal}; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * @sa connect(Type &) + * + * @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 pointer that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type *value_or_instance) { + disconnect(value_or_instance); + + delegate_type call{}; + call.template connect(value_or_instance); + signal_or_assert().calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return {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() { + delegate_type call{}; + call.template connect(); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects a free function with payload or a bound member from 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.
+ * 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 disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * + * @sa disconnect(Type &) + * + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + ENTT_ASSERT(value_or_instance != nullptr, "Invalid value or instance"); + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal_or_assert().calls.clear(); + } + + /** + * @brief Returns true if a sink is correctly initialized, false otherwise. + * @return True if a sink is correctly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return signal != nullptr; + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler type of a sink directly from the + * signal it refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +sink(sigh &) -> sink>; + +} // namespace entt + +#endif + +// IWYU pragma: end_exports \ No newline at end of file diff --git a/include/gui/GuiManager.h b/include/gui/GuiManager.h new file mode 100644 index 0000000..5fc2695 --- /dev/null +++ b/include/gui/GuiManager.h @@ -0,0 +1,155 @@ +#pragma once +#include "rlImGui.h" +#include "imgui.h" +#include "extras/IconsFontAwesome6.h" +#include "gui/fa-solid-900.h" +#include "gui/ImGuiNotify.hpp" +#include "LogWindow.h" // Include the new LogWindow class +#include "raylib.h" +#include +#include +#include +#include +#include + +class Application; + +class GuiManager { +public: + GuiManager(); + ~GuiManager(); + + void Initialize(Application* application); + void Render(RenderTexture2D& renderTexture); + void ApplyRenderTextureResize(RenderTexture2D& renderTexture); + void Shutdown(); + +private: + enum class AssetUnloadType { + None, + Texture, + Shader, + Model, + Material + }; + + void SetupDockspace(RenderTexture2D& renderTexture); + void RenderNotifications(); + void RenderErrorBanner(); + void SetTheme(); + void RenderSceneWindow(); + void RenderAssetWindow(); + void RenderInspectorWindow(); + void RenderEntityNode(entt::entity entity); + bool IsEntityAncestor(entt::entity ancestor, entt::entity entity) const; + const char* GetEntityLabel(entt::entity entity); + struct ScriptEditableFieldMeta { + std::string name; + std::string type; + std::string group; + std::string description; + bool hasRange = false; + float rangeMin = 0.0f; + float rangeMax = 1.0f; + std::vector options; + }; + + struct ScriptEditableCache { + std::string className; + std::vector fields; + std::int64_t lastWriteTime = 0; + bool hasWriteTime = false; + }; + + enum class ScriptValueKind { Float, Int, Bool, String }; + + struct ScriptEditableValue { + ScriptValueKind kind = ScriptValueKind::Float; + bool hasValue = false; + bool hasBaseline = false; + float f = 0.0f; + float baselineF = 0.0f; + int i = 0; + int baselineI = 0; + bool b = false; + bool baselineB = false; + std::string s; + std::string baselineS; + }; + + void SyncTagBuffer(entt::entity entity); + void SyncScriptPathBuffer(entt::entity entity); + void RenderScriptEditableControls(entt::entity entity); + ScriptEditableCache &GetOrParseEditableCache(const std::string &path, const std::string &className); + + bool showLogWindow = true; + bool showSceneWindow = true; + bool showInspectorWindow = true; + bool showAssetWindow = false; + Application *app; + LogWindow logWindow; // Add LogWindow as a member + entt::entity selectedEntity = entt::null; + entt::entity tagBufferEntity = entt::null; + char tagBuffer[128] = {0}; + entt::entity scriptPathEntity = entt::null; + char scriptPathBuffer[260] = {0}; + char sceneScriptPathBuffer[260] = {0}; + std::string lastSceneScriptPath; + ImVec2 desiredRenderSize = ImVec2(0.0f, 0.0f); + ImGuiID mainDockspaceId = 0; + bool requestSceneNew = false; + bool requestSceneLoad = false; + bool requestSceneSave = false; + char sceneLoadPath[260] = {0}; + char sceneSavePath[260] = {0}; + int addComponentIndex = 0; + + char textureKeyBuffer[128] = {0}; + char texturePathBuffer[260] = {0}; + int textureUnloadIndex = 0; + + char shaderKeyBuffer[128] = {0}; + char shaderVsPathBuffer[260] = {0}; + char shaderFsPathBuffer[260] = {0}; + int shaderUnloadIndex = 0; + + char modelKeyBuffer[128] = {0}; + char modelPathBuffer[260] = {0}; + char modelHeightmapPathBuffer[260] = {0}; + char modelCubicmapPathBuffer[260] = {0}; + int modelUnloadIndex = 0; + int modelPrimitiveIndex = 0; + float modelWidth = 1.0f; + float modelHeight = 1.0f; + float modelLength = 1.0f; + float modelRadius = 0.5f; + int modelRings = 16; + int modelSlices = 16; + int modelResX = 1; + int modelResZ = 1; + int modelRadSeg = 16; + int modelSides = 8; + int modelPolySides = 6; + float modelSizeX = 1.0f; + float modelSizeY = 1.0f; + float modelSizeZ = 1.0f; + int modelAutoKeyCounter = 0; + + char materialKeyBuffer[128] = {0}; + int materialUnloadIndex = 0; + int materialShaderIndex = 0; + int materialShaderId = 0; + float materialAlbedo[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + + AssetUnloadType pendingAssetUnloadType = AssetUnloadType::None; + std::string pendingAssetUnloadKey; + + Vector3 editorCameraPivot = {0.0f, 0.0f, 0.0f}; + Vector3 editorPivotOffset = {0.0f, 0.0f, 0.0f}; + entt::entity editorPivotEntity = entt::null; + bool editorPivotInitialized = false; + + std::unordered_map scriptEditableCache; + std::unordered_map> scriptEditableStaged; + std::unordered_map scriptEditableInstancePtr; +}; \ No newline at end of file diff --git a/include/gui/ImGuiNotify.hpp b/include/gui/ImGuiNotify.hpp index c633b40..e127a56 100644 --- a/include/gui/ImGuiNotify.hpp +++ b/include/gui/ImGuiNotify.hpp @@ -41,7 +41,7 @@ // Warning: Requires ImGui docking with multi-viewport enabled #ifndef NOTIFY_RENDER_OUTSIDE_MAIN_WINDOW -#define NOTIFY_RENDER_OUTSIDE_MAIN_WINDOW true // If true, the notifications will be rendered in the corner of the monitor, otherwise in the corner of the main window +#define NOTIFY_RENDER_OUTSIDE_MAIN_WINDOW false // If true, the notifications will be rendered in the corner of the monitor, otherwise in the corner of the main window #endif /** @@ -502,8 +502,9 @@ namespace ImGui } #if NOTIFY_RENDER_LIMIT > 0 - if (i > NOTIFY_RENDER_LIMIT) + if (i >= NOTIFY_RENDER_LIMIT) { + ++i; continue; } #endif diff --git a/include/LogWindow.h b/include/gui/LogWindow.h similarity index 100% rename from include/LogWindow.h rename to include/gui/LogWindow.h diff --git a/include/scripting/DrawBindings.h b/include/scripting/DrawBindings.h new file mode 100644 index 0000000..6482130 --- /dev/null +++ b/include/scripting/DrawBindings.h @@ -0,0 +1,5 @@ +#pragma once +#include + +void RegisterDrawBindings(asIScriptEngine *engine); + diff --git a/include/scripting/ECSBindings.h b/include/scripting/ECSBindings.h new file mode 100644 index 0000000..5036374 --- /dev/null +++ b/include/scripting/ECSBindings.h @@ -0,0 +1,8 @@ +#pragma once +#include +#include + +// Set the global registry that scripts will interact with +void SetGlobalRegistry(entt::registry* reg); + +void RegisterECSBindings(asIScriptEngine *engine); diff --git a/include/scripting/ImageBindings.h b/include/scripting/ImageBindings.h new file mode 100644 index 0000000..2c98785 --- /dev/null +++ b/include/scripting/ImageBindings.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void RegisterImageBindings(asIScriptEngine *engine); \ No newline at end of file diff --git a/include/scripting/InputBindings.h b/include/scripting/InputBindings.h new file mode 100644 index 0000000..17345bd --- /dev/null +++ b/include/scripting/InputBindings.h @@ -0,0 +1,7 @@ +#pragma once + +class asIScriptEngine; +class InputManager; + +void SetGlobalInputManager(InputManager* inputManager); +void RegisterInputBindings(asIScriptEngine* engine); diff --git a/include/scripting/LogBindings.h b/include/scripting/LogBindings.h new file mode 100644 index 0000000..d11d0fb --- /dev/null +++ b/include/scripting/LogBindings.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void RegisterLogBindings(asIScriptEngine *engine); diff --git a/include/scripting/MathBindings.h b/include/scripting/MathBindings.h new file mode 100644 index 0000000..d7a809f --- /dev/null +++ b/include/scripting/MathBindings.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void RegisterMathBindings(asIScriptEngine *engine); \ No newline at end of file diff --git a/include/scripting/PhysFSBindings.h b/include/scripting/PhysFSBindings.h new file mode 100644 index 0000000..fa3ecb0 --- /dev/null +++ b/include/scripting/PhysFSBindings.h @@ -0,0 +1,5 @@ +#pragma once + +class asIScriptEngine; + +void RegisterPhysFSBindings(asIScriptEngine* engine); diff --git a/include/scripting/ResourceBindings.h b/include/scripting/ResourceBindings.h new file mode 100644 index 0000000..78c92eb --- /dev/null +++ b/include/scripting/ResourceBindings.h @@ -0,0 +1,12 @@ +#pragma once + +class asIScriptEngine; +class ShaderManager; +class ModelManager; +class MaterialManager; +class TextureManager; +class AssetManager; + +void SetResourceManagers(ShaderManager* shaderMgr, ModelManager* modelMgr, MaterialManager* materialMgr, TextureManager* textureMgr); +void SetAssetManager(AssetManager* assetMgr); +void RegisterResourceBindings(asIScriptEngine *engine); diff --git a/include/scripting/SceneBindings.h b/include/scripting/SceneBindings.h new file mode 100644 index 0000000..5dece1d --- /dev/null +++ b/include/scripting/SceneBindings.h @@ -0,0 +1,5 @@ +#pragma once + +class asIScriptEngine; + +void RegisterSceneBindings(asIScriptEngine* engine); diff --git a/include/ScriptBindings.h b/include/scripting/ScriptBindings.h similarity index 100% rename from include/ScriptBindings.h rename to include/scripting/ScriptBindings.h diff --git a/include/scripting/ScriptEngine.h b/include/scripting/ScriptEngine.h new file mode 100644 index 0000000..dead221 --- /dev/null +++ b/include/scripting/ScriptEngine.h @@ -0,0 +1,58 @@ +#pragma once +#include "angelscript.h" +#include "scriptbuilder.h" +#include +#include + +class ScriptEngine { +public: + ScriptEngine(); + ~ScriptEngine(); + + bool Initialize(); + void Shutdown(); + bool CompileScript(const std::string& filename); + bool CompileModule(const std::string& moduleName, const std::string& filename, asIScriptModule** outModule); + bool CallModuleFunction(asIScriptModule* module, const std::string& decl); + void CallScriptFunction(asIScriptFunction* func, float dt = 0.0f); + void CallScriptFunction(asIScriptFunction* func, unsigned int entityId, float dt); + void GarbageCollect(); + + // Add a path used to resolve #include directives in scripts + void AddIncludePath(const std::string& path); + + // Try to preserve script state across hot-reloads by calling optional + // __SerializeState()/__DeserializeState() hooks in the script module. + std::string CaptureScriptState(); + void RestoreScriptState(const std::string &state); + + asIScriptEngine* GetEngine() const { return engine; } + asIScriptModule* GetCurrentModule() const { return currentModule; } + asIScriptFunction* GetUpdateFunction() const { return updateFunc; } + asIScriptFunction* GetInitFunction() const { return initFunc; } + asIScriptFunction* GetShutdownFunction() const { return shutdownFunc; } + + // Create and release script object instances for entity scripts + asIScriptObject* CreateObjectInstance(asIScriptModule* module, const std::string& className); + void ReleaseObjectInstance(asIScriptObject* obj); + // Retrieve an object's method by declaration (e.g. "void Update(uint, float)") + asIScriptFunction* GetObjectMethod(asIScriptModule* module, const std::string& className, const std::string& methodDecl); + // Call an object method; method must be an object method (use GetObjectMethod) + void CallObjectMethod(asIScriptFunction* method, asIScriptObject* object, unsigned int entityId, float dt); + // Call a scene object method (Init/Update/Shutdown) without entity id + void CallSceneObjectMethod(asIScriptFunction* method, asIScriptObject* object, float dt = 0.0f); + +private: + asIScriptEngine* engine; + asIScriptFunction* updateFunc; + asIScriptFunction* initFunc; + asIScriptFunction* shutdownFunc; + asIScriptModule* currentModule; + bool hasValidScript; + std::vector includePaths; + + static void MessageCallback(const asSMessageInfo* msg, void* param); + static int IncludeCallback(const char* include, const char* from, CScriptBuilder* builder, void* userParam); + std::string ReadFile(const std::string& filename); + void ClearCachedFunctions(); +}; \ No newline at end of file diff --git a/include/scripting/TextureBindings.h b/include/scripting/TextureBindings.h new file mode 100644 index 0000000..77ba37d --- /dev/null +++ b/include/scripting/TextureBindings.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void RegisterTextureBindings(asIScriptEngine *engine); diff --git a/include/scripting/ToastBindings.h b/include/scripting/ToastBindings.h new file mode 100644 index 0000000..ba7950d --- /dev/null +++ b/include/scripting/ToastBindings.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void RegisterToastBindings(asIScriptEngine *engine); diff --git a/scenes/demo.toml b/scenes/demo.toml new file mode 100644 index 0000000..b8601dc --- /dev/null +++ b/scenes/demo.toml @@ -0,0 +1,65 @@ +scene_script = "scripts/demo_scene.as" + +[[entity]] +id = "entity_1" +tag = "Root" +position = [0.000, 0.000, 0.000] +scale = [1.000, 1.000, 1.000] +rotation_deg = [0.000, 0.000, 0.000] + +[[entity]] +id = "entity_2" +parent = "entity_1" +tag = "Parent" +position = [2.000, -1.555, 0.000] +scale = [1.000, 1.000, 1.000] +rotation_deg = [0.000, 0.000, 0.000] +model_asset = "Torus" +model_id = 2 +color = 0x00FF00FF +outline_size = 0.000 +shader_vs = "shaders/toon.vs" +shader_fs = "shaders/toon.fs" +script = "scripts/ball.as" +script_var_phase = "0.000000" +script_var_speed = "0.699000" +script_var_amplitude = "1.748000" + +[[entity]] +id = "entity_3" +parent = "entity_2" +tag = "Child" +position = [0.500, -3.100, 1.600] +scale = [0.600, 0.600, 0.600] +rotation_deg = [0.000, 0.000, 0.000] +model = "sphere" +radius = 0.500 +rings = 16 +slices = 16 +color = 0xFF0000FF +outline_size = 0.000 +shader_vs = "shaders/toon.vs" +shader_fs = "shaders/toon.fs" +script = "scripts/ball.as" +script_var_amplitude = "1.452000" + +[[entity]] +id = "entity_6" +parent = "entity_2" +tag = "Child 2" +position = [0.000, 0.351, 1.900] +scale = [1.000, 1.000, 1.000] +rotation_deg = [0.000, 0.000, 0.000] +model = "sphere" +radius = 0.500 +rings = 16 +slices = 16 +color = 0x4523BAFF +outline_size = 0.000 +shader_vs = "shaders/toon.vs" +shader_fs = "shaders/toon.fs" +script = "scripts/ball.as" +script_var_amplitude = "0.568000" +script_var_phase = "0.472000" +script_var_speed = "10.000000" + diff --git a/scenes/demo2.toml b/scenes/demo2.toml new file mode 100644 index 0000000..bf80381 --- /dev/null +++ b/scenes/demo2.toml @@ -0,0 +1,49 @@ +scene_script = "scripts/demo_scene.as" + +[[entity]] +id = "entity_1048581" +tag = "Light" +position = [0.000, 0.000, 0.000] +scale = [1.000, 1.000, 1.000] +rotation_deg = [0.000, 0.000, 0.000] + +[[entity]] +id = "entity_1048576" +tag = "Camera" +position = [0.000, 0.000, 0.000] +scale = [1.000, 1.000, 1.000] +rotation_deg = [0.000, 0.000, 0.000] + +[[entity]] +id = "entity_1048578" +tag = "Root" +position = [0.000, 0.000, 0.000] +scale = [1.000, 1.000, 1.000] +rotation_deg = [0.000, 0.000, 0.000] + +[[entity]] +id = "entity_1048579" +tag = "Parent" +position = [0.000, 0.000, 0.000] +scale = [1.000, 1.000, 1.000] +rotation_deg = [0.000, 0.000, 0.000] +model_asset = "Torus" +model_id = 1 +color = 0xFFFFFFFF +outline_size = 0.000 + +[[entity]] +id = "entity_1048580" +parent = "entity_1048579" +tag = "Child" +position = [0.000, 1.400, 0.000] +scale = [1.000, 1.000, 1.000] +rotation_deg = [0.000, 0.000, 0.000] +model_asset = "demo_cube" +model = "cube" +model_size = [1.000, 1.000, 1.000] +color = 0xC11E1EFF +outline_size = 0.000 +shader_vs = "shaders/toon.vs" +shader_fs = "shaders/toon.fs" + diff --git a/scripts/as.predefined b/scripts/as.predefined index 1b40eb8..9c6d912 100644 --- a/scripts/as.predefined +++ b/scripts/as.predefined @@ -1,7 +1,21 @@ typedef void string; +string format(const string&in fmt, const ?&in ...); + +// Array template (provided by scriptarray add-on) +external shared class array { + uint length() const; + void resize(uint); + void insertAt(uint, const T&in); + void insertLast(const T&in); + void removeAt(uint); + void removeLast(); + T& opIndex(uint); + const T& opIndex(uint) const; +} + +// Logging functions void Print(const string&in); void Log(int level, const string&in); -void DrawText(const string&in, int x, int y, int fontSize, int color); int LOG_TRACE; int LOG_DEBUG; int LOG_INFO; @@ -9,9 +23,273 @@ int LOG_WARNING; int LOG_ERROR; int LOG_FATAL; +// Toast notification functions namespace Toast { - void Info(const string&in); + void Info(const string&in msg); void Warning(const string&in); void Error(const string&in); void Success(const string&in); } + +// ECS functions +namespace ECS { + // Entity management + uint CreateEntity(); + void DestroyEntity(uint entity); + bool IsValid(uint entity); + + // Transform component + void AddTransform(uint entity, float x, float y, float z); + void SetPosition(uint entity, float x, float y, float z); + void GetPosition(uint entity, float &out x, float &out y, float &out z); + void SetScale(uint entity, float x, float y, float z); + void SetRotation(uint entity, float rotation); + void SetRotationEuler(uint entity, float x, float y, float z); + float GetRotation(uint entity); + void GetRotationEuler(uint entity, float &out x, float &out y, float &out z); + bool HasTransform(uint entity); + void RemoveTransform(uint entity); + + // Velocity component + void AddVelocity(uint entity, float vx, float vy, float vz, float angular = 0.0f); + void SetVelocity(uint entity, float vx, float vy, float vz); + void SetAngularVelocity(uint entity, float angular); + bool HasVelocity(uint entity); + void RemoveVelocity(uint entity); + + // ModelRenderer component + void AddModelRenderer(uint entity, int modelId, uint color, float outlineSize = 0.0f); + void SetModelRendererColor(uint entity, uint color); + void SetModelRendererModel(uint entity, int modelId); + void SetModelRendererOutline(uint entity, float outlineSize); + void SetModelRendererShader(uint entity, uint shaderId); + bool HasModelRenderer(uint entity); + void RemoveModelRenderer(uint entity); + + // Texture component + void AddTexture(uint entity, uint textureId); + void SetTextureId(uint entity, uint textureId); + uint GetTextureId(uint entity); + bool HasTexture(uint entity); + void RemoveTexture(uint entity); + + // UV transform component + void AddUVTransform(uint entity, float scaleX, float scaleY, float offsetX = 0.0f, float offsetY = 0.0f, float rotation = 0.0f); + void SetUVScale(uint entity, float scaleX, float scaleY); + void SetUVOffset(uint entity, float offsetX, float offsetY); + void SetUVRotation(uint entity, float rotation); + bool HasUVTransform(uint entity); + void RemoveUVTransform(uint entity); + + // Tag component + void AddTag(uint entity, const string &in name); + string GetTag(uint entity); + void SetTag(uint entity, const string &in name); + bool HasTag(uint entity); + void RemoveTag(uint entity); + + // Hierarchy component + void SetParent(uint child, uint parent); + void ClearParent(uint child); + uint GetParent(uint child); + bool HasParent(uint child); + + // Camera3D component + void AddCamera3D(uint entity, float px, float py, float pz, float tx, float ty, float tz, float fovy); + void SetCameraPosition(uint entity, float x, float y, float z); + void SetCameraTarget(uint entity, float x, float y, float z); + void SetCameraFovy(uint entity, float fovy); + bool HasCamera3D(uint entity); + void RemoveCamera3D(uint entity); + + // Light component + void AddLight(uint entity, float dx, float dy, float dz, float intensity = 1.0f); + void SetLightDirection(uint entity, float x, float y, float z); + void SetLightIntensity(uint entity, float intensity); + void SetLightColor(uint entity, uint color); + bool HasLight(uint entity); + void RemoveLight(uint entity); + + // Material component + void AddMaterial(uint entity, uint materialId); + void SetMaterialId(uint entity, uint materialId); + uint GetMaterialId(uint entity); + bool HasMaterial(uint entity); + void RemoveMaterial(uint entity); + + // RenderPass component + void AddRenderPass(uint entity, int order, bool depthTest, bool depthWrite, uint shaderId); + void SetRenderPassOrder(uint entity, int order); + void SetRenderPassShader(uint entity, uint shaderId); + bool HasRenderPass(uint entity); + void RemoveRenderPass(uint entity); +} + +// Shader resource management +namespace Shader { + uint Load(const string &in vsPath, const string &in fsPath); + void Unload(uint id); +} + +// Model resource management +namespace Model { + uint Load(const string &in path); + uint LoadCube(float width, float height, float length); + uint LoadSphere(float radius, int rings, int slices); + uint LoadPlane(float width, float length, int resX, int resZ); + void Unload(uint id); +} + +// Material resource management +namespace Material { + uint Create(uint shaderId, uint8 r, uint8 g, uint8 b, uint8 a); + void Remove(uint id); + void SetShader(uint id, uint shaderId); + void SetAlbedo(uint id, uint8 r, uint8 g, uint8 b, uint8 a); +} + +// Asset manager (keyed assets) +namespace Asset { + uint LoadTexture(const string &in key, const string &in path); + uint LoadShader(const string &in key, const string &in vsPath, const string &in fsPath); + uint LoadModel(const string &in key, const string &in path); + uint LoadCube(const string &in key, float width, float height, float length); + uint LoadPoly(const string &in key, int sides, float radius); + uint LoadSphere(const string &in key, float radius, int rings, int slices); + uint LoadHemiSphere(const string &in key, float radius, int rings, int slices); + uint LoadPlane(const string &in key, float width, float length, int resX, int resZ); + uint LoadCylinder(const string &in key, float radius, float height, int slices); + uint LoadCone(const string &in key, float radius, float height, int slices); + uint LoadTorus(const string &in key, float radius, float size, int radSeg, int sides); + uint LoadKnot(const string &in key, float radius, float size, int radSeg, int sides); + uint LoadHeightmap(const string &in key, const string &in heightmapPath, float sizeX, float sizeY, float sizeZ); + uint LoadCubicmap(const string &in key, const string &in cubicmapPath, float sizeX, float sizeY, float sizeZ); + uint CreateMaterial(const string &in key, uint shaderId, uint8 r, uint8 g, uint8 b, uint8 a); + bool UnloadTexture(const string &in key); + bool UnloadShader(const string &in key); + bool UnloadModel(const string &in key); + bool UnloadMaterial(const string &in key); + uint GetTextureId(const string &in key); + uint GetShaderId(const string &in key); + uint GetModelId(const string &in key); + uint GetMaterialId(const string &in key); + bool ApplyMeshTangents(const string &in key, int meshIndex = 0); +} + +namespace Scene { + uint Load(const string &in path, bool clearExisting = true); + void Clear(); +} + +namespace Texture { + class Image { + int width; + int height; + int mipmaps; + int format; + }; + Image@ LoadImage(const string&in path); + void Unload(const Image@&in image); + class Texture2D { + int id; + int width; + int height; + int mipmaps; + int format; + }; + Texture2D@ LoadTexture(const string&in path); + Texture2D@ LoadFromImage(const Image@&in image); + void Draw(const Texture2D@&in texture, int x, int y, int color); + void Unload(const Texture2D@&in texture); +} + +namespace Math { + float Sin(float angle); + float Cos(float angle); + float Tan(float angle); + float ASin(float value); + float ACos(float value); + float ATan(float value); + float ATan2(float y, float x); + float Sqrt(float value); + float Pow(float base, float exponent); + float Log(float value); + float Log10(float value); + float Exp(float exponent); + float Floor(float value); + float Ceil(float value); + float Modf(float x, float y); + float Abs(float value); +} + +// Raylib input and timing functions +float GetTime(); +bool IsKeyPressed(int key); + +// Input Action System +bool IsActionPressed(const string &in actionName); +bool IsActionDown(const string &in actionName); +bool IsActionReleased(const string &in actionName); +float GetActionStrength(const string &in actionName); +void RegisterAction(const string &in actionName); +void BindKeyboard(const string &in actionName, int keyCode); +void BindMouseButton(const string &in actionName, int mouseButton); +void BindGamepadButton(const string &in actionName, int gamepadId, int button); +void BindGamepadAxis(const string &in actionName, int gamepadId, int axis, float threshold = 0.5f, bool negative = false); +void ClearBindings(const string &in actionName); +void ClearAllActions(); + +namespace Key { + const int SPACE = 32; + const int ESCAPE = 256; + const int ENTER = 257; + const int TAB = 258; + const int BACKSPACE = 259; + const int RIGHT = 262; + const int LEFT = 263; + const int DOWN = 264; + const int UP = 265; + const int W = 87; + const int A = 65; + const int S = 83; + const int D = 68; + const int E = 69; + const int LEFT_CONTROL = 341; +} + +namespace MouseButton { + const int LEFT = 0; + const int RIGHT = 1; + const int MIDDLE = 2; +} + +namespace GamepadButton { + const int UNKNOWN = 0; + const int LEFT_FACE_UP = 1; + const int LEFT_FACE_RIGHT = 2; + const int LEFT_FACE_DOWN = 3; + const int LEFT_FACE_LEFT = 4; + const int RIGHT_FACE_UP = 5; + const int RIGHT_FACE_RIGHT = 6; + const int RIGHT_FACE_DOWN = 7; + const int RIGHT_FACE_LEFT = 8; + const int LEFT_TRIGGER_1 = 9; + const int LEFT_TRIGGER_2 = 10; + const int RIGHT_TRIGGER_1 = 11; + const int RIGHT_TRIGGER_2 = 12; + const int MIDDLE_LEFT = 13; + const int MIDDLE = 14; + const int MIDDLE_RIGHT = 15; + const int LEFT_THUMB = 16; + const int RIGHT_THUMB = 17; +} + +namespace GamepadAxis { + const int LEFT_X = 0; + const int LEFT_Y = 1; + const int RIGHT_X = 2; + const int RIGHT_Y = 3; + const int LEFT_TRIGGER = 4; + const int RIGHT_TRIGGER = 5; +} + diff --git a/scripts/autoload.as b/scripts/autoload.as new file mode 100644 index 0000000..7c5cb34 --- /dev/null +++ b/scripts/autoload.as @@ -0,0 +1,40 @@ +// Autoload configuration script. +// Runs before any scene/game script to configure input and preload resources. + +void Autoload() { + // Pick the initial scene to load. + Scene::Load("scenes/demo.toml"); + + ClearAllActions(); + + // Movement + RegisterAction("move_up"); + RegisterAction("move_down"); + RegisterAction("move_left"); + RegisterAction("move_right"); + BindKeyboard("move_up", Key::W); + BindKeyboard("move_up", Key::UP); + BindKeyboard("move_down", Key::S); + BindKeyboard("move_down", Key::DOWN); + BindKeyboard("move_left", Key::A); + BindKeyboard("move_left", Key::LEFT); + BindKeyboard("move_right", Key::D); + BindKeyboard("move_right", Key::RIGHT); + + // Actions + RegisterAction("jump"); + RegisterAction("interact"); + RegisterAction("attack"); + BindKeyboard("jump", Key::SPACE); + BindKeyboard("interact", Key::E); + BindKeyboard("attack", Key::LEFT_CONTROL); + BindMouseButton("attack", MouseButton::LEFT); + + // Gamepad (optional) + BindGamepadButton("jump", 0, GamepadButton::RIGHT_FACE_DOWN); + BindGamepadButton("interact", 0, GamepadButton::RIGHT_FACE_RIGHT); + BindGamepadAxis("move_left", 0, GamepadAxis::LEFT_X, 0.3f, true); + BindGamepadAxis("move_right", 0, GamepadAxis::LEFT_X, 0.3f, false); + + // TODO: Preload assets and other config here. +} diff --git a/scripts/ball.as b/scripts/ball.as new file mode 100644 index 0000000..b94dcec --- /dev/null +++ b/scripts/ball.as @@ -0,0 +1,49 @@ +// Class-based entity script: one `EntityScript` instance per entity. +// Implements Init/Update/Shutdown so the engine will instantiate it per-entity. + +class EntityScript +{ + float baseX; + float baseY; + float baseZ; + [group["Movement"]][desc["Phase accumulator for the sine wave"]][editable][range[0,1]]float phase; + + // per-instance movement parameters (can be tuned per-entity) + [group["Movement"]][desc["Bounce height"]][editable][range[0,10]]float amplitude = 2.5f; + [group["Movement"]][desc["Cycles per second"]][editable][range[0,10]]float speed = 2.5f; + + [group["State"]][desc["Debug animation state"]][editable][options[Idle,Walk,Run]] int state = 0; + + EntityScript() + { + baseX = 0.0f; + baseY = 0.0f; + baseZ = 0.0f; + phase = 0.0f; + } + + void Init(uint entity) + { + if (!ECS::IsValid(entity)) return; + if (!ECS::HasTransform(entity)) + ECS::AddTransform(entity, 0.0f, 0.0f, 0.0f); + + ECS::GetPosition(entity, baseX, baseY, baseZ); + phase = 0.0f; + } + + void Update(uint entity, float dt) + { + if (!ECS::IsValid(entity)) return; + if (!ECS::HasTransform(entity)) return; + + phase += dt; + float offset = Math::Sin(phase * speed + float(entity) * 0.1f) * amplitude; + ECS::SetPosition(entity, baseX, baseY + offset, baseZ); + } + + void Shutdown(uint entity) + { + // nothing to clean up per-instance for this simple example + } +} diff --git a/scripts/demo_scene.as b/scripts/demo_scene.as new file mode 100644 index 0000000..0b23fdc --- /dev/null +++ b/scripts/demo_scene.as @@ -0,0 +1,52 @@ +// Demo scene script with input-driven behavior. +class SceneScript { + uint sceneRoot = 0; + uint camera = 0; + uint light = 0; + uint cube = 0; + float time = 0.0f; + bool highlight = false; + + void Init() { + Log(LOG_INFO, "Demo scene script init"); + + cube = ECS::CreateEntity(); + ECS::AddTransform(cube, 0.0f, 1.5f, 0.0f); + uint modelId = Asset::GetModelId("demo_cube"); + if (modelId == 0) { + modelId = Asset::LoadCube("demo_cube", 1.0f, 1.0f, 1.0f); + } + ECS::AddModelRenderer(cube, int(modelId), 0xFFAA00FF, 0.0f); + ECS::AddTag(cube, "InputCube"); + } + + void Shutdown() { + Log(LOG_INFO, "Demo scene script shutdown"); + } + + void Update(float dt) { + time += dt; + + if (ECS::IsValid(cube) && ECS::HasTransform(cube)) { + ECS::SetRotationEuler(cube, 0.0f, time, time * 0.5f); + } + + if (IsActionPressed("jump")) { + highlight = !highlight; + if (highlight) { + ECS::SetModelRendererColor(cube, 0x00FF88FF); + ECS::SetModelRendererOutline(cube, 2.0f); + Toast::Info("Highlight on"); + } else { + ECS::SetModelRendererColor(cube, 0xFFAA00FF); + ECS::SetModelRendererOutline(cube, 0.0f); + Toast::Info("Highlight off"); + } + } + + if (IsActionPressed("attack")) { + ECS::SetLightDirection(light, -0.5f, 0.7f, -0.3f); + Toast::Success("Light toggled"); + } + } +} diff --git a/scripts/example_rendering.as b/scripts/example_rendering.as new file mode 100644 index 0000000..1c2ea78 --- /dev/null +++ b/scripts/example_rendering.as @@ -0,0 +1,101 @@ +// Example script showing how to use the new script-driven rendering system + +// Global variables to track our resources +uint toonShader = 0; +uint outlineShader = 0; +uint cubeModel = 0; +uint sphereModel = 0; +uint redMaterial = 0; +uint blueMaterial = 0; + +// Entities +uint camera = 0; +uint light = 0; +uint cube = 0; +uint sphere = 0; + +void Init() { + Toast::Info("Initializing script-driven rendering..."); + + // === Load Shaders === + toonShader = Shader::Load("shaders/toon.vs", "shaders/toon.fs"); + outlineShader = Shader::Load("shaders/outline.vs", "shaders/outline.fs"); + + if (toonShader == 0 || outlineShader == 0) { + Toast::Error("Failed to load shaders!"); + return; + } + + // === Load Models === + cubeModel = Model::LoadCube(1.0, 1.0, 1.0); + sphereModel = Model::LoadSphere(0.5, 16, 16); + + // === Create Materials === + // Red material with toon shader + redMaterial = Material::Create(toonShader, 255, 100, 100, 255); + + // Blue material with toon shader + blueMaterial = Material::Create(toonShader, 100, 100, 255, 255); + + // === Set up Camera === + camera = ECS::CreateEntity(); + ECS::AddCamera3D(camera, + 5.0, 5.0, 5.0, // position + 0.0, 0.0, 0.0, // target + 45.0); // fovy + ECS::AddTag(camera, "MainCamera"); + + // === Set up Light === + light = ECS::CreateEntity(); + ECS::AddLight(light, 0.5, 0.7, 0.3, 1.0); // direction + intensity + ECS::AddTag(light, "MainLight"); + + // === Create Cube with Outline === + cube = ECS::CreateEntity(); + ECS::AddTransform(cube, -1.5, 0.5, 0.0); + ECS::AddModelRenderer(cube, cubeModel, 0xFFFFFFFF, 0.0); + ECS::AddMaterial(cube, redMaterial); + ECS::AddTag(cube, "Cube"); + ECS::AddVelocity(cube, 0.0, 0.0, 0.0, 1.0); // rotating + + // Add outline render pass (order 0 = first) + ECS::AddRenderPass(cube, 0, false, true, outlineShader); // order=0, no depth test + + // === Create Sphere === + sphere = ECS::CreateEntity(); + ECS::AddTransform(sphere, 1.5, 0.5, 0.0); + ECS::AddModelRenderer(sphere, sphereModel, 0xFFFFFFFF, 0.0); + ECS::AddMaterial(sphere, blueMaterial); + ECS::AddTag(sphere, "Sphere"); + ECS::AddVelocity(sphere, 0.5, 0.0, 0.2, 0.5); // moving and rotating + + // Add main render pass (order 1 = second, after outlines) + ECS::AddRenderPass(sphere, 1, true, true, 0); // order=1, with depth test + + Toast::Success("Script-driven rendering initialized!"); +} +bool lightToggle = false; +void Update(float dt) { + // Camera orbital movement + if (ECS::HasCamera3D(camera)) { + float angle = GetTime() * 0.3; + float radius = 7.0; + float x = Math::Cos(angle) * radius; + float z = Math::Sin(angle) * radius; + ECS::SetCameraPosition(camera, x, 5.0, z); + } + + // Example: Toggle light direction with space + if (IsKeyPressed(Key::SPACE)) { + + lightToggle = !lightToggle; + + if (lightToggle) { + ECS::SetLightDirection(light, -0.5, 0.7, -0.3); + Toast::Info("Light direction changed!"); + } else { + ECS::SetLightDirection(light, 0.5, 0.7, 0.3); + Toast::Info("Light direction reset!"); + } + } +} diff --git a/scripts/game.as b/scripts/game.as index 734eb8f..374ac4f 100644 --- a/scripts/game.as +++ b/scripts/game.as @@ -1,5 +1,7 @@ -#include "update.as" - -void Draw() { - DrawText("Hello from AngelScript - Working perfectly!", int(x), int(y), 20, 0xFF0000FF); -} \ No newline at end of file +// Empty scene script to avoid auto-loading demo content. +class SceneScript { + void Init() {} + void Shutdown() {} + void Update(float dt) { + } +} diff --git a/scripts/scene_example.as b/scripts/scene_example.as new file mode 100644 index 0000000..95f9948 --- /dev/null +++ b/scripts/scene_example.as @@ -0,0 +1,20 @@ +// Example scene script using a class instance. +class SceneScript { + float time = 0.0f; + + void Init() { + Log(LOG_INFO, "SceneScript.Init (example)"); + } + + void Update(float dt) { + time += dt; + if (time > 1.0f) { + time = 0.0f; + Log(LOG_DEBUG, "SceneScript.Update tick"); + } + } + + void Shutdown() { + Log(LOG_INFO, "SceneScript.Shutdown (example)"); + } +} diff --git a/scripts/sync_wiki.sh b/scripts/sync_wiki.sh new file mode 100644 index 0000000..f6b93ed --- /dev/null +++ b/scripts/sync_wiki.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Script to sync local docs/ to the Gitea wiki for this repo. +# Requires SSH access to the wiki (deploy key or account key). + +WIKI_REPO="git@gitea.appstack.me:nick/simian.wiki.git" + +TMPDIR="$(mktemp -d)" +echo "Cloning wiki to $TMPDIR" +git clone "$WIKI_REPO" "$TMPDIR" + +echo "Syncing docs/ -> wiki/" +rsync -av --delete docs/ "$TMPDIR"/ + +pushd "$TMPDIR" > /dev/null +git add -A +if git diff --staged --quiet; then + echo "No changes to wiki" +else + git commit -m "Update wiki from docs" + git push origin HEAD +fi +popd > /dev/null + +rm -rf "$TMPDIR" +echo "Done." diff --git a/scripts/update.as b/scripts/update.as deleted file mode 100644 index 237741a..0000000 --- a/scripts/update.as +++ /dev/null @@ -1,12 +0,0 @@ -float x = 50; -float y = 100; - -void Update(float dt) { - x += 64 * dt; - if (x > 800) { - x = 0; - Print("X position reset!"); - Log(LOG_INFO, "Log INFO: reset happened"); - Toast::Info("X position has been reset."); - } -} diff --git a/shaders/outline.fs b/shaders/outline.fs new file mode 100644 index 0000000..7cdc0d5 --- /dev/null +++ b/shaders/outline.fs @@ -0,0 +1,9 @@ +#version 330 + +out vec4 fragColor; + +uniform vec4 outlineColor; // Color of the outline + +void main() { + fragColor = outlineColor; +} \ No newline at end of file diff --git a/shaders/outline.vs b/shaders/outline.vs new file mode 100644 index 0000000..a83baa8 --- /dev/null +++ b/shaders/outline.vs @@ -0,0 +1,21 @@ +#version 330 + +layout(location = 0) in vec3 vertexPosition; +layout(location = 2) in vec3 vertexNormal; + +uniform mat4 mvp; +uniform mat4 matModel; +uniform mat4 matNormal; +uniform float outlineThickness; + +void main() { + // Transform normal to world space + vec3 worldNormal = normalize(vec3(matNormal * vec4(vertexNormal, 0.0))); + + // Expand vertices along normals in world space + vec3 worldPos = vec3(matModel * vec4(vertexPosition, 1.0)); + vec3 expandedPos = worldPos + worldNormal * outlineThickness; + + // Transform to clip space + gl_Position = mvp * vec4(vertexPosition + vertexNormal * outlineThickness, 1.0); +} \ No newline at end of file diff --git a/shaders/toon.fs b/shaders/toon.fs new file mode 100644 index 0000000..5d3b6a3 --- /dev/null +++ b/shaders/toon.fs @@ -0,0 +1,22 @@ +#version 330 + +in vec3 fragNormal; +in vec3 fragPosition; + +uniform vec4 albedo; +uniform vec3 lightDir; +out vec4 fragColor; + +void main() { + // Use uniform light direction, with fallback + vec3 light = normalize(lightDir); + float bandCount = 3.0; + + vec3 normal = normalize(fragNormal); + + float intensity = max(dot(normal, light), 0.0); + intensity = floor(intensity * bandCount) / bandCount; + intensity = max(intensity, 0.3); // Minimum visibility + + fragColor = vec4(albedo.rgb * intensity, albedo.a); +} \ No newline at end of file diff --git a/shaders/toon.vs b/shaders/toon.vs new file mode 100644 index 0000000..5564e00 --- /dev/null +++ b/shaders/toon.vs @@ -0,0 +1,27 @@ +#version 330 + +// Raylib's default attribute locations +layout(location = 0) in vec3 vertexPosition; +layout(location = 1) in vec2 vertexTexCoord; +layout(location = 2) in vec3 vertexNormal; +layout(location = 3) in vec4 vertexColor; + +// Raylib's built-in uniforms (automatically provided) +uniform mat4 mvp; // Model-View-Projection matrix +uniform mat4 matModel; // Model matrix +uniform mat4 matNormal; // Normal matrix for transforming normals + +out vec3 fragNormal; +out vec3 fragPosition; + +void main() { + // Transform position + vec4 worldPos = matModel * vec4(vertexPosition, 1.0); + fragPosition = worldPos.xyz; + + // Transform normal properly + fragNormal = normalize(vec3(matNormal * vec4(vertexNormal, 0.0))); + + // Final position + gl_Position = mvp * vec4(vertexPosition, 1.0); +} \ No newline at end of file diff --git a/src/Application.cpp b/src/Application.cpp index be4ba14..cd4b0ac 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -1,28 +1,57 @@ #include "Application.h" +#include "ECSComponents.h" +#include "scripting/ECSBindings.h" +#include "scripting/ResourceBindings.h" +#include "SceneGraph.h" +#include "SceneLoader.h" +#include "PhysFSManager.h" #include "raylib.h" +#include "rlgl.h" +#include "raymath.h" #include #include #include -#include +#include +#include #include "log/log.h" #include "rlImGui.h" #include "imgui.h" -#include "extras/IconsFontAwesome6.h" -#include "gui/ImGuiNotify.hpp" -#include "GuiManager.h" +#include "gui/GuiManager.h" #include "gui/Toast.h" +#include "physfs.h" -#ifdef _WIN32 -// On Windows, fopen_s is already available, so no need to define it. -#else -// On non-Windows platforms, define fopen_s as a macro for fopen. -#define fopen_s(pFile, filename, mode) ((*(pFile) = fopen((filename), (mode))) == NULL) -#endif const char *Application::WINDOW_TITLE = "Simian"; -const char *Application::SCRIPT_FILE = "scripts/game.as"; +static const char* kAutoloadScriptFile = "scripts/autoload.as"; +static const char* kAssetRegistryFile = "assets/registry.toml"; +static const char* kLogFilePath = "log.txt"; -Application::Application() : hotReload(nullptr), scriptCompilationError(false), logFile(nullptr), renderTexture{} // Initialize renderTexture +static void PhysFSLogCallback(log_Event* ev) +{ + if (!ev || !ev->udata) return; + PHYSFS_File* file = static_cast(ev->udata); + + char buf[64]; + buf[strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", ev->time)] = '\0'; + + std::string line; + if (ev->file) { + line = std::string(buf) + " " + log_level_string(ev->level) + " " + ev->file + ":" + std::to_string(ev->line) + ": "; + } else { + line = std::string(buf) + " " + log_level_string(ev->level) + ": "; + } + + char message[1024]; + vsnprintf(message, sizeof(message), ev->fmt, ev->ap); + line += message; + line.push_back('\n'); + + PHYSFS_writeBytes(file, line.data(), static_cast(line.size())); + PHYSFS_flush(file); +} + +Application::Application() + : hotReload(nullptr), scriptCompilationError(false), logFile(nullptr), renderTexture{} { } @@ -35,14 +64,24 @@ Application::~Application() bool Application::Initialize(int argc, char *argv[]) { - if (fopen_s(&logFile, "log.txt", "w") != 0) + logFile = nullptr; + + if (!PhysFSManager::Initialize(argc > 0 ? argv[0] : nullptr)) { - logFile = nullptr; - log_error("Failed to open log file"); + log_error("Failed to initialize PhysFS"); + return false; } - else - { - log_add_fp(logFile, LOG_TRACE); + + PhysFSManager::SetWriteDir("."); + + const char* mountDirs[] = {"scripts", "scenes", "assets", "shaders", "fonts", "textures", "models"}; + for (const char* dir : mountDirs) { + PhysFSManager::MountPath(dir, dir, true); + } + + logFile = PhysFSManager::OpenWrite(kLogFilePath); + if (logFile) { + log_add_callback(PhysFSLogCallback, logFile, LOG_TRACE); } SetTraceLogCallback(raylib_log); @@ -57,7 +96,9 @@ bool Application::Initialize(int argc, char *argv[]) enableEditor = true; } } + // Initialize Raylib + SetConfigFlags(FLAG_WINDOW_RESIZABLE); InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE); SetTargetFPS(TARGET_FPS); @@ -68,34 +109,68 @@ bool Application::Initialize(int argc, char *argv[]) return false; } + // Set up ECS bindings with our registry + SetGlobalRegistry(®istry); + + // Set up resource managers for script access + extern void SetResourceManagers(ShaderManager*, ModelManager*, MaterialManager*, TextureManager*); + SetResourceManagers(&shaderManager, &modelManager, &materialManager, &textureManager); + + extern void SetAssetManager(AssetManager*); + SetAssetManager(&assetManager); + + assetManager.SetManagers(&modelManager, &shaderManager, &materialManager, &textureManager); + + // Set up scene loader context + SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager); + SetSceneAssetManager(&assetManager); + + assetManager.LoadRegistry(kAssetRegistryFile); + + // Set up input manager for script access + extern void SetGlobalInputManager(InputManager*); + SetGlobalInputManager(&inputManager); + + currentSceneScriptPath.clear(); + + if (!RunAutoloadScript(true)) + { + log_error("Autoload script failed - aborting startup"); + return false; + } + // Initialize hot reload to watch the scripts directory so any script change // triggers a reload. { - std::filesystem::path p(SCRIPT_FILE); - std::string watchPath = p.parent_path().string(); - if (watchPath.empty()) + std::string watchPath = kAutoloadScriptFile; + size_t sep = watchPath.find_last_of("/\\"); + if (sep != std::string::npos) { + watchPath = watchPath.substr(0, sep); + } else { watchPath = "."; + } hotReload = new HotReload(watchPath); } - // Compile initial script - scriptCompilationError = !scriptEngine.CompileScript(SCRIPT_FILE); - if (enableEditor) { - guiManager.Initialize(this); - renderTexture = LoadRenderTexture(WINDOW_WIDTH, WINDOW_HEIGHT); } + editorCamera.position = {5.0f, 5.0f, 5.0f}; + editorCamera.target = {0.0f, 0.0f, 0.0f}; + editorCamera.up = {0.0f, 1.0f, 0.0f}; + editorCamera.fovy = 45.0f; + editorCamera.projection = CAMERA_PERSPECTIVE; + return true; } void Application::Run() { - - while (!WindowShouldClose()) + ProcessSceneScriptChanges(); + while (!WindowShouldClose() && !queueShutdown) { float deltaTime = GetFrameTime(); @@ -109,70 +184,727 @@ void Application::Run() void Application::Update(float deltaTime) { + ProcessSceneScriptChanges(); + // Check for hot reload - if (hotReload && hotReload->CheckForChanges()) - { - bool success = scriptEngine.CompileScript(SCRIPT_FILE); - scriptCompilationError = !success; - if (!success) - { - log_warn("Script compilation failed - keeping previous version"); - Toast::Warning("Script compilation failed - check console for details."); - } else { - Toast::Success("Script reloaded successfully."); + if (hotReload) { + bool changed = hotReload->CheckForChanges(); + if (changed) { + if (!RunAutoloadScript(false)) { + log_warn("Autoload script failed during reload - keeping previous config"); + } + + bool scriptChanged = ProcessSceneScriptChanges(); + if (!scriptChanged && !currentSceneScriptPath.empty()) { + bool success = scriptEngine.CompileScript(currentSceneScriptPath); + scriptCompilationError = !success; + if (!success) + { + log_warn("Script compilation failed - keeping previous version"); + Toast::Warning("Script compilation failed - check console for details."); + } + else + { + Toast::Success("Script reloaded successfully."); + // Should call init function again or things don't work + InitializeSceneScriptInstance(); + } + } + + // Refresh any entity script modules so class-based entity scripts + // pick up changes immediately (release old instances then recompile). + ReloadEntityScriptModules(); } } - // Call script Update function + // Call scene script Update (class instance or fallback) + CallSceneUpdate(deltaTime); + + UpdateEntityScripts(deltaTime); + + // Update ECS systems + UpdateSystems(deltaTime); +} + +bool Application::RunAutoloadScript(bool abortOnFailure) +{ + asIScriptModule* module = nullptr; + if (!scriptEngine.CompileModule("autoload", kAutoloadScriptFile, &module) || !module) { + log_error("Failed to compile autoload script: %s", kAutoloadScriptFile); + return !abortOnFailure; + } + + if (!scriptEngine.CallModuleFunction(module, "void Autoload()")) { + log_error("Failed to execute autoload script: %s", kAutoloadScriptFile); + return !abortOnFailure; + } + + return true; +} + +void Application::UpdateSystems(float deltaTime) +{ + // Movement system - apply velocity to transform + auto view = registry.view(); + view.each([deltaTime](auto &transform, auto &velocity) + { + // Apply linear velocity + transform.position.x += velocity.linear.x * deltaTime; + transform.position.y += velocity.linear.y * deltaTime; + transform.position.z += velocity.linear.z * deltaTime; + + // Apply angular velocity (default to Y axis) + transform.rotation.y += velocity.angular * deltaTime; }); + + UpdateWorldTransforms(registry); +} + +static bool ParseOverrideBool(const std::string& value, bool& out) +{ + std::string lower; + lower.reserve(value.size()); + for (char c : value) { + lower.push_back(static_cast(std::tolower(static_cast(c)))); + } + if (lower == "true" || lower == "1") { out = true; return true; } + if (lower == "false" || lower == "0") { out = false; return true; } + return false; +} + +static void ApplyScriptOverrides(ScriptEngine& scriptEngine, ScriptComponent& script, asIScriptObject* instance) +{ + if (!instance || script.scriptOverrides.empty()) return; + asIScriptEngine* engine = scriptEngine.GetEngine(); + if (!engine) return; + + const int floatTypeId = engine->GetTypeIdByDecl("float"); + const int intTypeId = engine->GetTypeIdByDecl("int"); + const int boolTypeId = engine->GetTypeIdByDecl("bool"); + const int stringTypeId = engine->GetTypeIdByDecl("string"); + + const int propertyCount = static_cast(instance->GetPropertyCount()); + for (const auto& entry : script.scriptOverrides) { + const std::string& name = entry.first; + const std::string& raw = entry.second; + + int propIndex = -1; + int propTypeId = 0; + for (int i = 0; i < propertyCount; ++i) { + const char* propName = instance->GetPropertyName(i); + if (propName && name == propName) { + propIndex = i; + propTypeId = instance->GetPropertyTypeId(i); + break; + } + } + if (propIndex < 0) continue; + void* propAddr = instance->GetAddressOfProperty(propIndex); + if (!propAddr) continue; + + try { + if (propTypeId == floatTypeId) { + *reinterpret_cast(propAddr) = std::stof(raw); + } else if (propTypeId == intTypeId) { + *reinterpret_cast(propAddr) = std::stoi(raw); + } else if (propTypeId == boolTypeId) { + bool b = false; + if (ParseOverrideBool(raw, b)) { + *reinterpret_cast(propAddr) = b; + } + } else if (propTypeId == stringTypeId) { + *reinterpret_cast(propAddr) = raw; + } + } catch (...) { + // Ignore parse errors; leave the script value as-is. + } + } +} + +Application::EntityScriptModule* Application::GetEntityScriptModule(const std::string& path) +{ + if (path.empty()) { + return nullptr; + } + + auto it = entityScriptModules.find(path); + if (it != entityScriptModules.end()) { + return &it->second; + } + + EntityScriptModule moduleInfo; + moduleInfo.path = path; + moduleInfo.moduleName = "entity_script_" + std::to_string(entityScriptModuleCounter++); + + if (!scriptEngine.CompileModule(moduleInfo.moduleName, path, &moduleInfo.module)) { + log_warn("Failed to compile entity script: %s", path.c_str()); + entityScriptModules.emplace(path, moduleInfo); + return &entityScriptModules[path]; + } + + if (moduleInfo.module) { + // Try to detect an object type to instantiate for per-entity scripts. + // Prefer an explicit type named 'EntityScript' or any type that + // implements the expected entity methods. Do NOT pick the first + // available type as that may be an add-on type (array/string/etc). + asITypeInfo* objType = nullptr; + int count = moduleInfo.module->GetObjectTypeCount(); + for (int i = 0; i < count; ++i) { + asITypeInfo* t = moduleInfo.module->GetObjectTypeByIndex(i); + const char* n = t->GetName(); + if (!n) continue; + std::string name(n); + // Prefer explicit EntityScript class name + if (name == "EntityScript") { objType = t; break; } + // Otherwise, accept any script class that exposes Update/Init/Shutdown + // (at least one of the methods must exist) + bool hasInit = (t->GetMethodByDecl("void Init(uint)") != nullptr); + bool hasUpdate = (t->GetMethodByDecl("void Update(uint, float)") != nullptr); + bool hasShutdown = (t->GetMethodByDecl("void Shutdown(uint)") != nullptr); + if (hasInit || hasUpdate || hasShutdown) { + objType = t; + break; + } + } + + if (objType) { + moduleInfo.objectType = objType; + const char* n = objType->GetName(); + moduleInfo.objectTypeName = n ? n : std::string(); + + // Cache object-methods (decls must match what's used in scripts) + moduleInfo.initFunc = objType->GetMethodByDecl("void Init(uint)"); + moduleInfo.updateFunc = objType->GetMethodByDecl("void Update(uint, float)"); + moduleInfo.shutdownFunc = objType->GetMethodByDecl("void Shutdown(uint)"); + } else { + // Also accept free functions for backward compatibility + moduleInfo.initFunc = moduleInfo.module->GetFunctionByDecl("void EntityInit(uint)"); + if (!moduleInfo.initFunc) { + moduleInfo.initFunc = moduleInfo.module->GetFunctionByDecl("void Init(uint)"); + } + + moduleInfo.updateFunc = moduleInfo.module->GetFunctionByDecl("void EntityUpdate(uint, float)"); + if (!moduleInfo.updateFunc) { + moduleInfo.updateFunc = moduleInfo.module->GetFunctionByDecl("void Update(uint, float)"); + } + + moduleInfo.shutdownFunc = moduleInfo.module->GetFunctionByDecl("void EntityShutdown(uint)"); + if (!moduleInfo.shutdownFunc) { + moduleInfo.shutdownFunc = moduleInfo.module->GetFunctionByDecl("void Shutdown(uint)"); + } + } + + moduleInfo.valid = true; + } + + entityScriptModules.emplace(path, moduleInfo); + return &entityScriptModules[path]; +} + +void Application::UpdateEntityScripts(float deltaTime) +{ + auto view = registry.view(); + std::vector invalidBindings; + + for (auto entity : view) { + auto& script = view.get(entity); + if (!script.enabled || script.scriptPath.empty()) { + auto existingBinding = entityScriptBindings.find(entity); + if (script.started && existingBinding != entityScriptBindings.end()) { + auto* moduleInfo = GetEntityScriptModule(existingBinding->second); + if (moduleInfo && moduleInfo->valid) { + // If using object instances, call method on instance and release + if (script.scriptInstance && moduleInfo->shutdownFunc) { + scriptEngine.CallObjectMethod(moduleInfo->shutdownFunc, reinterpret_cast(script.scriptInstance), static_cast(entity), 0.0f); + scriptEngine.ReleaseObjectInstance(reinterpret_cast(script.scriptInstance)); + script.scriptInstance = nullptr; + } else if (moduleInfo->shutdownFunc) { + // Fallback to free-function shutdown + scriptEngine.CallScriptFunction(moduleInfo->shutdownFunc, static_cast(entity), 0.0f); + } + } + } + script.started = false; + entityScriptBindings.erase(entity); + continue; + } + + std::string previousPath; + auto existingBinding = entityScriptBindings.find(entity); + if (existingBinding != entityScriptBindings.end()) { + previousPath = existingBinding->second; + } + + if (previousPath != script.scriptPath) { + if (!previousPath.empty()) { + auto* oldModule = GetEntityScriptModule(previousPath); + if (oldModule && oldModule->valid) { + if (script.scriptInstance && oldModule->shutdownFunc) { + scriptEngine.CallObjectMethod(oldModule->shutdownFunc, reinterpret_cast(script.scriptInstance), static_cast(entity), 0.0f); + scriptEngine.ReleaseObjectInstance(reinterpret_cast(script.scriptInstance)); + script.scriptInstance = nullptr; + } else if (oldModule->shutdownFunc) { + scriptEngine.CallScriptFunction(oldModule->shutdownFunc, static_cast(entity), 0.0f); + } + } + } + entityScriptBindings[entity] = script.scriptPath; + script.started = false; + } + + auto* moduleInfo = GetEntityScriptModule(script.scriptPath); + if (!moduleInfo || !moduleInfo->valid) { + continue; + } + + if (!script.started) { + // Create per-entity instance if object-type based + if (!script.scriptInstance && moduleInfo->objectType) { + std::string classToCreate = !script.className.empty() ? script.className : moduleInfo->objectTypeName; + asIScriptObject* inst = scriptEngine.CreateObjectInstance(moduleInfo->module, classToCreate); + if (inst) { + script.scriptInstance = reinterpret_cast(inst); + // Call Init method if present + if (moduleInfo->initFunc) { + scriptEngine.CallObjectMethod(moduleInfo->initFunc, inst, static_cast(entity), 0.0f); + } + ApplyScriptOverrides(scriptEngine, script, inst); + } else { + // Fallback: call free-function init if exists + if (moduleInfo->initFunc) { + scriptEngine.CallScriptFunction(moduleInfo->initFunc, static_cast(entity), 0.0f); + } + } + } else { + // No object instance model; use existing free-function init + if (moduleInfo->initFunc) { + scriptEngine.CallScriptFunction(moduleInfo->initFunc, static_cast(entity), 0.0f); + } + } + script.started = true; + } + + if (moduleInfo->updateFunc) { + if (script.scriptInstance && moduleInfo->objectType) { + scriptEngine.CallObjectMethod(moduleInfo->updateFunc, reinterpret_cast(script.scriptInstance), static_cast(entity), deltaTime); + } else { + scriptEngine.CallScriptFunction(moduleInfo->updateFunc, static_cast(entity), deltaTime); + } + } + } + + for (const auto& [entity, path] : entityScriptBindings) { + if (!registry.valid(entity)) { + invalidBindings.push_back(entity); + } + } + + for (auto entity : invalidBindings) { + entityScriptBindings.erase(entity); + } +} + +void Application::ShutdownEntityScripts() +{ + auto view = registry.view(); + for (auto entity : view) { + auto& script = view.get(entity); + if (!script.started || script.scriptPath.empty()) { + continue; + } + + auto* moduleInfo = GetEntityScriptModule(script.scriptPath); + if (moduleInfo && moduleInfo->valid) { + if (script.scriptInstance && moduleInfo->shutdownFunc) { + scriptEngine.CallObjectMethod(moduleInfo->shutdownFunc, reinterpret_cast(script.scriptInstance), static_cast(entity), 0.0f); + scriptEngine.ReleaseObjectInstance(reinterpret_cast(script.scriptInstance)); + script.scriptInstance = nullptr; + } else if (moduleInfo->shutdownFunc) { + scriptEngine.CallScriptFunction(moduleInfo->shutdownFunc, static_cast(entity), 0.0f); + } + } + script.started = false; + } +} + +void Application::InitializeSceneScriptInstance() +{ + ShutdownSceneScriptInstance(); + + asIScriptModule* module = scriptEngine.GetCurrentModule(); + if (!module) { + return; + } + + asITypeInfo* objType = nullptr; + asIScriptFunction* initMethod = nullptr; + asIScriptFunction* updateMethod = nullptr; + asIScriptFunction* shutdownMethod = nullptr; + + const int count = module->GetObjectTypeCount(); + for (int i = 0; i < count; ++i) { + asITypeInfo* typeInfo = module->GetObjectTypeByIndex(i); + if (!typeInfo) { + continue; + } + if ((typeInfo->GetFlags() & asOBJ_SCRIPT_OBJECT) == 0) { + continue; + } + + asIScriptFunction* typeInit = typeInfo->GetMethodByDecl("void SceneInit()"); + if (!typeInit) { + typeInit = typeInfo->GetMethodByDecl("void Init()"); + } + asIScriptFunction* typeUpdate = typeInfo->GetMethodByDecl("void SceneUpdate(float)"); + if (!typeUpdate) { + typeUpdate = typeInfo->GetMethodByDecl("void Update(float)"); + } + asIScriptFunction* typeShutdown = typeInfo->GetMethodByDecl("void SceneShutdown()"); + if (!typeShutdown) { + typeShutdown = typeInfo->GetMethodByDecl("void Shutdown()"); + } + + if (typeInit || typeUpdate || typeShutdown) { + objType = typeInfo; + initMethod = typeInit; + updateMethod = typeUpdate; + shutdownMethod = typeShutdown; + break; + } + } + + if (objType) { + const char* name = objType->GetName(); + sceneScriptInstance.className = name ? name : std::string(); + sceneScriptInstance.initFunc = initMethod; + sceneScriptInstance.updateFunc = updateMethod; + sceneScriptInstance.shutdownFunc = shutdownMethod; + sceneScriptInstance.instance = scriptEngine.CreateObjectInstance(module, sceneScriptInstance.className); + + if (sceneScriptInstance.instance) { + if (sceneScriptInstance.initFunc) { + scriptEngine.CallSceneObjectMethod(sceneScriptInstance.initFunc, sceneScriptInstance.instance, 0.0f); + } + return; + } + } + + // Fallback to free functions when no class-based scene script is available. + scriptEngine.CallScriptFunction(scriptEngine.GetInitFunction()); +} + +void Application::ShutdownSceneScriptInstance() +{ + if (sceneScriptInstance.instance) { + if (sceneScriptInstance.shutdownFunc) { + scriptEngine.CallSceneObjectMethod(sceneScriptInstance.shutdownFunc, sceneScriptInstance.instance, 0.0f); + } + scriptEngine.ReleaseObjectInstance(sceneScriptInstance.instance); + sceneScriptInstance.instance = nullptr; + } + + sceneScriptInstance.className.clear(); + sceneScriptInstance.initFunc = nullptr; + sceneScriptInstance.updateFunc = nullptr; + sceneScriptInstance.shutdownFunc = nullptr; +} + +void Application::CallSceneUpdate(float deltaTime) +{ + if (sceneScriptInstance.instance && sceneScriptInstance.updateFunc) { + scriptEngine.CallSceneObjectMethod(sceneScriptInstance.updateFunc, sceneScriptInstance.instance, deltaTime); + return; + } scriptEngine.CallScriptFunction(scriptEngine.GetUpdateFunction(), deltaTime); } +bool Application::ProcessSceneScriptChanges() +{ + std::string sceneScriptPath; + if (!ConsumeSceneScriptPath(sceneScriptPath)) { + return false; + } + + if (sceneScriptPath.empty()) { + if (!currentSceneScriptPath.empty()) { + currentSceneScriptPath.clear(); + ShutdownSceneScriptInstance(); + scriptCompilationError = false; + } + return true; + } + + if (sceneScriptPath == currentSceneScriptPath) { + return false; + } + + bool success = scriptEngine.CompileScript(sceneScriptPath); + scriptCompilationError = !success; + if (!success) { + log_warn("Scene script compilation failed - keeping previous version"); + Toast::Warning("Scene script compilation failed - check console for details."); + return true; + } + + currentSceneScriptPath = sceneScriptPath; + InitializeSceneScriptInstance(); + return true; +} + +void Application::ReloadEntityScriptModules() +{ + // Iterate all known entity script modules. For each, call shutdown on + // existing instances, release them, then recompile the module and + // refresh cached method pointers. + for (auto &kv : entityScriptModules) { + auto &moduleInfo = kv.second; + if (moduleInfo.path.empty()) continue; + + log_info("Reloading entity module: %s", moduleInfo.path.c_str()); + // For every entity bound to this path, release its current instance + for (auto it = entityScriptBindings.begin(); it != entityScriptBindings.end(); ++it) { + if (it->second != moduleInfo.path) continue; + entt::entity e = it->first; + if (!registry.valid(e)) continue; + auto &script = registry.get(e); + if (script.scriptInstance) { + // Call old module shutdown method if available + if (moduleInfo.shutdownFunc) { + scriptEngine.CallObjectMethod(moduleInfo.shutdownFunc, reinterpret_cast(script.scriptInstance), static_cast(e), 0.0f); + } + // Release the old instance + scriptEngine.ReleaseObjectInstance(reinterpret_cast(script.scriptInstance)); + script.scriptInstance = nullptr; + script.started = false; + } + } + + // Recompile module in-place using the same moduleName so the engine + // replaces the module's bytecode and types. + asIScriptModule* newModule = nullptr; + bool ok = scriptEngine.CompileModule(moduleInfo.moduleName, moduleInfo.path, &newModule); + moduleInfo.module = newModule; + moduleInfo.valid = ok && (moduleInfo.module != nullptr); + if (moduleInfo.valid) { + log_info("Reloaded entity module: %s", moduleInfo.path.c_str()); + } else { + log_warn("Failed to reload entity module: %s", moduleInfo.path.c_str()); + } + + // Re-detect object type and cached methods (same logic as in GetEntityScriptModule) + moduleInfo.objectType = nullptr; + moduleInfo.objectTypeName.clear(); + moduleInfo.initFunc = nullptr; + moduleInfo.updateFunc = nullptr; + moduleInfo.shutdownFunc = nullptr; + + if (moduleInfo.module) { + asITypeInfo* objType = nullptr; + int count = moduleInfo.module->GetObjectTypeCount(); + for (int i = 0; i < count; ++i) { + asITypeInfo* t = moduleInfo.module->GetObjectTypeByIndex(i); + const char* n = t->GetName(); + if (!n) continue; + std::string name(n); + if (name == "EntityScript") { objType = t; break; } + bool hasInit = (t->GetMethodByDecl("void Init(uint)") != nullptr); + bool hasUpdate = (t->GetMethodByDecl("void Update(uint, float)") != nullptr); + bool hasShutdown = (t->GetMethodByDecl("void Shutdown(uint)") != nullptr); + if (hasInit || hasUpdate || hasShutdown) { objType = t; break; } + } + + if (objType) { + moduleInfo.objectType = objType; + const char* n = objType->GetName(); + moduleInfo.objectTypeName = n ? n : std::string(); + moduleInfo.initFunc = objType->GetMethodByDecl("void Init(uint)"); + moduleInfo.updateFunc = objType->GetMethodByDecl("void Update(uint, float)"); + moduleInfo.shutdownFunc = objType->GetMethodByDecl("void Shutdown(uint)"); + } else { + moduleInfo.initFunc = moduleInfo.module->GetFunctionByDecl("void EntityInit(uint)"); + if (!moduleInfo.initFunc) moduleInfo.initFunc = moduleInfo.module->GetFunctionByDecl("void Init(uint)"); + moduleInfo.updateFunc = moduleInfo.module->GetFunctionByDecl("void EntityUpdate(uint, float)"); + if (!moduleInfo.updateFunc) moduleInfo.updateFunc = moduleInfo.module->GetFunctionByDecl("void Update(uint, float)"); + moduleInfo.shutdownFunc = moduleInfo.module->GetFunctionByDecl("void EntityShutdown(uint)"); + if (!moduleInfo.shutdownFunc) moduleInfo.shutdownFunc = moduleInfo.module->GetFunctionByDecl("void Shutdown(uint)"); + } + } + } +} + +void Application::RenderScene() +{ + // Query for active camera (first one found with Camera3DComponent) + Camera3D camera = {}; + bool hasCamera = false; + + if (useEditorCamera) { + camera = editorCamera; + hasCamera = true; + } else { + auto cameraView = registry.view(); + cameraView.each([&](auto& camComp) { + if (!hasCamera) { + camera.position = camComp.position; + camera.target = camComp.target; + camera.up = camComp.up; + camera.fovy = camComp.fovy; + camera.projection = camComp.projection; + hasCamera = true; + } + }); + } + + // If no camera found, use a default one + if (!hasCamera) { + camera.position = {5, 5, 5}; + camera.target = {0, 0, 0}; + camera.up = {0, 1, 0}; + camera.fovy = 45; + camera.projection = CAMERA_PERSPECTIVE; + } + + // Find the first light or use a default + Vector3 lightDir = {0.5f, 0.7f, 0.3f}; + auto lightView = registry.view(); + lightView.each([&](auto& lightComp) { + lightDir = Vector3Normalize(lightComp.direction); + }); + + BeginMode3D(camera); + DrawGrid(10, 1); + + // Render all entities with WorldTransform and ModelRenderer components + auto view = registry.view(); + view.each([&](auto entity, auto& transform, auto& renderer) { + if (renderer.modelId <= 0) { + return; + } + + Model* model = modelManager.Get(static_cast(renderer.modelId)); + if (!model) { + return; + } + + Texture2D originalTexture = {}; + bool restoreTexture = false; + if (auto* tex = registry.try_get(entity)) { + if (tex->textureId > 0) { + Texture2D* texture = textureManager.Get(tex->textureId); + if (texture && model->materialCount > 0) { + originalTexture = model->materials[0].maps[MATERIAL_MAP_DIFFUSE].texture; + model->materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = *texture; + restoreTexture = true; + } + } + } + + // If model renderer has a shader, apply it with uniforms + if (renderer.shaderId > 0) { + Shader* shader = shaderManager.Get(static_cast(renderer.shaderId)); + if (shader) { + // Set shader uniforms + int albedoLoc = GetShaderLocation(*shader, "albedo"); + int lightDirLoc = GetShaderLocation(*shader, "lightDir"); + + if (albedoLoc != -1) { + Vector4 albedo = { + renderer.color.r / 255.0f, + renderer.color.g / 255.0f, + renderer.color.b / 255.0f, + renderer.color.a / 255.0f + }; + SetShaderValue(*shader, albedoLoc, &albedo, SHADER_UNIFORM_VEC4); + } + + if (lightDirLoc != -1) { + SetShaderValue(*shader, lightDirLoc, &lightDir, SHADER_UNIFORM_VEC3); + } + + Vector2 uvScale = {1.0f, 1.0f}; + Vector2 uvOffset = {0.0f, 0.0f}; + float uvRotation = 0.0f; + if (auto* uv = registry.try_get(entity)) { + uvScale = uv->scale; + uvOffset = uv->offset; + uvRotation = uv->rotation; + } + + int uvScaleLoc = GetShaderLocation(*shader, "uvScale"); + if (uvScaleLoc != -1) { + SetShaderValue(*shader, uvScaleLoc, &uvScale, SHADER_UNIFORM_VEC2); + } + int uvOffsetLoc = GetShaderLocation(*shader, "uvOffset"); + if (uvOffsetLoc != -1) { + SetShaderValue(*shader, uvOffsetLoc, &uvOffset, SHADER_UNIFORM_VEC2); + } + int uvRotationLoc = GetShaderLocation(*shader, "uvRotation"); + if (uvRotationLoc != -1) { + SetShaderValue(*shader, uvRotationLoc, &uvRotation, SHADER_UNIFORM_FLOAT); + } + + // Assign shader to model's first material + model->materials[0].shader = *shader; + } + } + + // Draw model using ModelRenderer.color as the tint + Quaternion rotation = QuaternionFromEuler(transform.rotation.x, transform.rotation.y, transform.rotation.z); + Vector3 axis = {0.0f, 1.0f, 0.0f}; + float angle = 0.0f; + QuaternionToAxisAngle(rotation, &axis, &angle); + float axisLength = sqrtf(axis.x * axis.x + axis.y * axis.y + axis.z * axis.z); + if (axisLength <= 0.0001f) { + axis = {0.0f, 1.0f, 0.0f}; + angle = 0.0f; + } + + DrawModelEx(*model, transform.position, axis, + angle * RAD2DEG, transform.scale, + renderer.color); + + if (restoreTexture) { + model->materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = originalTexture; + } + }); + + EndMode3D(); +} + void Application::Draw() { + int screenWidth = GetScreenWidth(); + int screenHeight = GetScreenHeight(); + + // Fallback to default values if screen dimensions are invalid + if (screenWidth <= 0 || screenHeight <= 0) + { + log_warn("Invalid screen dimensions: %d x %d. Using default values.", screenWidth, screenHeight); + screenWidth = 800; + screenHeight = 600; + } + if (enableEditor) { - int screenWidth = GetScreenWidth(); - int screenHeight = GetScreenHeight(); - - // Fallback to default values if screen dimensions are invalid - if (screenWidth <= 0 || screenHeight <= 0) - { - log_warn("Invalid screen dimensions: %d x %d. Using default values.", screenWidth, screenHeight); - screenWidth = 800; - screenHeight = 600; - } - ImGuiIO &io = ImGui::GetIO(); - io.DisplaySize = ImVec2((float)screenWidth, (float)screenHeight); // Set DisplaySize + io.DisplaySize = ImVec2((float)screenWidth, (float)screenHeight); + + guiManager.ApplyRenderTextureResize(renderTexture); BeginTextureMode(renderTexture); - ClearBackground(RAYWHITE); - - // Call script Draw function - scriptEngine.CallScriptFunction(scriptEngine.GetDrawFunction()); - + ClearBackground(SKYBLUE); + RenderScene(); EndTextureMode(); + BeginDrawing(); ClearBackground(RAYWHITE); - - guiManager.Render(renderTexture); // Render the GUI - + guiManager.Render(renderTexture); EndDrawing(); } else { BeginDrawing(); - ClearBackground(RAYWHITE); - - // Show script error status in top left if there's an error, otherwise show normal message - if (scriptCompilationError) - { - DrawText("SCRIPT ERROR - Check console for details", 10, 10, 16, RED); - } - - // Call script Draw function - scriptEngine.CallScriptFunction(scriptEngine.GetDrawFunction()); - + ClearBackground(SKYBLUE); + RenderScene(); EndDrawing(); } } @@ -185,6 +917,9 @@ void Application::Shutdown() hotReload = nullptr; } + + ShutdownEntityScripts(); + ShutdownSceneScriptInstance(); scriptEngine.Shutdown(); if (enableEditor) { @@ -194,8 +929,16 @@ void Application::Shutdown() // Shutdown rlImGui first while the GL context and window are still valid rlImGuiShutdown(); - // Shutdown script engine - scriptEngine.Shutdown(); + // Clear ECS registry first to release all component data + registry.clear(); + + // Unload all resources before closing window (while GL context is still valid) + materialManager.Clear(); + assetManager.SaveRegistry(kAssetRegistryFile); + assetManager.Clear(); + shaderManager.UnloadAll(); + modelManager.UnloadAll(); + textureManager.UnloadAll(); // Clear the trace log callback before closing window to prevent logging after cleanup SetTraceLogCallback(nullptr); @@ -204,7 +947,9 @@ void Application::Shutdown() // Close log file after everything else is cleaned up if (logFile) { - fclose(logFile); + PhysFSManager::Close(logFile); logFile = nullptr; } -} \ No newline at end of file + + PhysFSManager::Shutdown(); +} diff --git a/src/AssetManager.cpp b/src/AssetManager.cpp new file mode 100644 index 0000000..812dd26 --- /dev/null +++ b/src/AssetManager.cpp @@ -0,0 +1,1083 @@ +#include "AssetManager.h" +#include "ModelManager.h" +#include "ShaderManager.h" +#include "MaterialManager.h" +#include "TextureManager.h" +#include "PhysFSManager.h" +#include "log/log.h" +#include +#include +#include +#include + +namespace { +std::string TrimAsset(const std::string& value) { + size_t start = 0; + while (start < value.size() && std::isspace(static_cast(value[start]))) { + start++; + } + size_t end = value.size(); + while (end > start && std::isspace(static_cast(value[end - 1]))) { + end--; + } + return value.substr(start, end - start); +} + +std::string StripCommentAsset(const std::string& line) { + bool inString = false; + for (size_t i = 0; i < line.size(); ++i) { + char c = line[i]; + if (c == '"') { + inString = !inString; + } + if (!inString && c == '#') { + return line.substr(0, i); + } + } + return line; +} + +bool ParseStringAsset(const std::string& value, std::string& out) { + std::string trimmed = TrimAsset(value); + if (trimmed.size() >= 2 && trimmed.front() == '"' && trimmed.back() == '"') { + out = trimmed.substr(1, trimmed.size() - 2); + return true; + } + return false; +} + +bool ParseFloatList(const std::string& value, std::vector& out) { + std::string trimmed = TrimAsset(value); + if (trimmed.size() < 2 || trimmed.front() != '[' || trimmed.back() != ']') { + return false; + } + + std::string inner = trimmed.substr(1, trimmed.size() - 2); + std::stringstream ss(inner); + std::string token; + out.clear(); + + while (std::getline(ss, token, ',')) { + std::string piece = TrimAsset(token); + if (piece.empty()) { + continue; + } + try { + out.push_back(std::stof(piece)); + } catch (...) { + return false; + } + } + + return !out.empty(); +} + +std::string EscapeStringAsset(const std::string& value) { + std::string out; + out.reserve(value.size()); + for (char c : value) { + if (c == '\\' || c == '"') { + out.push_back('\\'); + } + out.push_back(c); + } + return out; +} + +void WriteFloatList(std::ostream& out, const std::vector& values) { + out << '['; + for (size_t i = 0; i < values.size(); ++i) { + if (i > 0) { + out << ", "; + } + out << values[i]; + } + out << ']'; +} +} + +AssetManager::AssetManager() = default; + +AssetManager::~AssetManager() = default; + +void AssetManager::SetManagers(ModelManager* modelMgr, ShaderManager* shaderMgr, + MaterialManager* materialMgr, TextureManager* textureMgr) +{ + modelManager = modelMgr; + shaderManager = shaderMgr; + materialManager = materialMgr; + textureManager = textureMgr; +} + +bool AssetManager::RegisterTexture(const std::string& key, unsigned int id, const std::string& source) +{ + if (key.empty() || id == 0) { + return false; + } + if (textureAssets.find(key) != textureAssets.end()) { + return textureAssets[key].id == id; + } + + textureAssets[key] = AssetEntry{id, source}; + textureIds[id] = key; + return true; +} + +bool AssetManager::RegisterShader(const std::string& key, unsigned int id, const std::string& source) +{ + if (key.empty() || id == 0) { + return false; + } + if (shaderAssets.find(key) != shaderAssets.end()) { + return shaderAssets[key].id == id; + } + + shaderAssets[key] = AssetEntry{id, source}; + shaderIds[id] = key; + return true; +} + +bool AssetManager::RegisterModel(const std::string& key, unsigned int id, const std::string& source) +{ + if (key.empty() || id == 0) { + return false; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id == id; + } + + modelAssets[key] = AssetEntry{id, source}; + modelIds[id] = key; + return true; +} + +bool AssetManager::RegisterMaterial(const std::string& key, unsigned int id, const std::string& source) +{ + if (key.empty() || id == 0) { + return false; + } + if (materialAssets.find(key) != materialAssets.end()) { + return materialAssets[key].id == id; + } + + materialAssets[key] = AssetEntry{id, source}; + materialIds[id] = key; + return true; +} + +unsigned int AssetManager::LoadTexture(const std::string& key, const std::string& path) +{ + if (!textureManager) { + log_warn("AssetManager: texture manager not set"); + return 0; + } + if (textureAssets.find(key) != textureAssets.end()) { + return textureAssets[key].id; + } + + unsigned int id = textureManager->Load(path); + if (id != 0) { + RegisterTexture(key, id, path); + textureDefs[key] = TextureDefinition{path}; + } + return id; +} + +unsigned int AssetManager::LoadShader(const std::string& key, const std::string& vsPath, const std::string& fsPath) +{ + if (!shaderManager) { + log_warn("AssetManager: shader manager not set"); + return 0; + } + if (shaderAssets.find(key) != shaderAssets.end()) { + return shaderAssets[key].id; + } + + unsigned int id = shaderManager->Load(vsPath, fsPath); + if (id != 0) { + RegisterShader(key, id, vsPath + "|" + fsPath); + shaderDefs[key] = ShaderDefinition{vsPath, fsPath}; + } + return id; +} + +unsigned int AssetManager::LoadModel(const std::string& key, const std::string& path) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + unsigned int id = modelManager->Load(path); + if (id != 0) { + RegisterModel(key, id, path); + ModelDefinition def; + def.type = ModelDefinition::Type::Path; + def.path = path; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadCube(const std::string& key, float width, float height, float length) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + unsigned int id = modelManager->LoadCube(width, height, length); + if (id != 0) { + RegisterModel(key, id, "cube"); + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = "cube"; + def.params = {width, height, length}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadPoly(const std::string& key, int sides, float radius) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + Mesh mesh = GenMeshPoly(sides, radius); + unsigned int id = modelManager->LoadFromMesh(mesh); + if (id != 0) { + RegisterModel(key, id, "poly"); + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = "poly"; + def.params = {static_cast(sides), radius}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadSphere(const std::string& key, float radius, int rings, int slices) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + unsigned int id = modelManager->LoadSphere(radius, rings, slices); + if (id != 0) { + RegisterModel(key, id, "sphere"); + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = "sphere"; + def.params = {radius, static_cast(rings), static_cast(slices)}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadHemiSphere(const std::string& key, float radius, int rings, int slices) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + Mesh mesh = GenMeshHemiSphere(radius, rings, slices); + unsigned int id = modelManager->LoadFromMesh(mesh); + if (id != 0) { + RegisterModel(key, id, "hemisphere"); + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = "hemisphere"; + def.params = {radius, static_cast(rings), static_cast(slices)}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadPlane(const std::string& key, float width, float length, int resX, int resZ) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + unsigned int id = modelManager->LoadPlane(width, length, resX, resZ); + if (id != 0) { + RegisterModel(key, id, "plane"); + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = "plane"; + def.params = {width, length, static_cast(resX), static_cast(resZ)}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadCylinder(const std::string& key, float radius, float height, int slices) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + Mesh mesh = GenMeshCylinder(radius, height, slices); + unsigned int id = modelManager->LoadFromMesh(mesh); + if (id != 0) { + RegisterModel(key, id, "cylinder"); + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = "cylinder"; + def.params = {radius, height, static_cast(slices)}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadCone(const std::string& key, float radius, float height, int slices) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + Mesh mesh = GenMeshCone(radius, height, slices); + unsigned int id = modelManager->LoadFromMesh(mesh); + if (id != 0) { + RegisterModel(key, id, "cone"); + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = "cone"; + def.params = {radius, height, static_cast(slices)}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadTorus(const std::string& key, float radius, float size, int radSeg, int sides) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + Mesh mesh = GenMeshTorus(radius, size, radSeg, sides); + unsigned int id = modelManager->LoadFromMesh(mesh); + if (id != 0) { + RegisterModel(key, id, "torus"); + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = "torus"; + def.params = {radius, size, static_cast(radSeg), static_cast(sides)}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadKnot(const std::string& key, float radius, float size, int radSeg, int sides) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + Mesh mesh = GenMeshKnot(radius, size, radSeg, sides); + unsigned int id = modelManager->LoadFromMesh(mesh); + if (id != 0) { + RegisterModel(key, id, "knot"); + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = "knot"; + def.params = {radius, size, static_cast(radSeg), static_cast(sides)}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadHeightmap(const std::string& key, const std::string& heightmapPath, float sizeX, float sizeY, float sizeZ) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + Image heightmap = LoadImage(heightmapPath.c_str()); + if (!heightmap.data) { + log_warn("AssetManager: failed to load heightmap %s", heightmapPath.c_str()); + return 0; + } + + Mesh mesh = GenMeshHeightmap(heightmap, Vector3{sizeX, sizeY, sizeZ}); + UnloadImage(heightmap); + + unsigned int id = modelManager->LoadFromMesh(mesh); + if (id != 0) { + RegisterModel(key, id, "heightmap:" + heightmapPath); + ModelDefinition def; + def.type = ModelDefinition::Type::Heightmap; + def.path = heightmapPath; + def.params = {sizeX, sizeY, sizeZ}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadCubicmap(const std::string& key, const std::string& cubicmapPath, float sizeX, float sizeY, float sizeZ) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + Image cubicmap = LoadImage(cubicmapPath.c_str()); + if (!cubicmap.data) { + log_warn("AssetManager: failed to load cubicmap %s", cubicmapPath.c_str()); + return 0; + } + + Mesh mesh = GenMeshCubicmap(cubicmap, Vector3{sizeX, sizeY, sizeZ}); + UnloadImage(cubicmap); + + unsigned int id = modelManager->LoadFromMesh(mesh); + if (id != 0) { + RegisterModel(key, id, "cubicmap:" + cubicmapPath); + ModelDefinition def; + def.type = ModelDefinition::Type::Cubicmap; + def.path = cubicmapPath; + def.params = {sizeX, sizeY, sizeZ}; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::LoadFromMesh(const std::string& key, Mesh mesh, const std::string& source) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return 0; + } + if (modelAssets.find(key) != modelAssets.end()) { + return modelAssets[key].id; + } + + unsigned int id = modelManager->LoadFromMesh(mesh); + if (id != 0) { + RegisterModel(key, id, source); + ModelDefinition def; + def.type = ModelDefinition::Type::Mesh; + def.path = source; + modelDefs[key] = def; + } + return id; +} + +unsigned int AssetManager::CreateMaterial(const std::string& key, unsigned int shaderId, Color albedo) +{ + if (!materialManager) { + log_warn("AssetManager: material manager not set"); + return 0; + } + if (materialAssets.find(key) != materialAssets.end()) { + return materialAssets[key].id; + } + + unsigned int id = materialManager->Create(shaderId, albedo); + if (id != 0) { + RegisterMaterial(key, id, "shader_id=" + std::to_string(shaderId)); + MaterialDefinition def; + def.albedo = albedo; + if (shaderId != 0) { + const std::string& shaderKey = GetShaderKey(shaderId); + if (!shaderKey.empty()) { + def.shaderKey = shaderKey; + def.hasShaderKey = true; + } else { + def.shaderId = shaderId; + def.hasShaderId = true; + } + } + materialDefs[key] = def; + } + return id; +} + +bool AssetManager::ApplyMeshTangents(const std::string& key, int meshIndex) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return false; + } + + unsigned int id = GetModelId(key); + if (id == 0) { + return false; + } + + Model* model = modelManager->Get(id); + if (!model || model->meshCount <= 0) { + return false; + } + + if (meshIndex < 0 || meshIndex >= model->meshCount) { + return false; + } + + GenMeshTangents(&model->meshes[meshIndex]); + UploadMesh(&model->meshes[meshIndex], false); + return true; +} + +bool AssetManager::UnloadTexture(const std::string& key) +{ + if (!textureManager) { + log_warn("AssetManager: texture manager not set"); + return false; + } + auto it = textureAssets.find(key); + if (it == textureAssets.end()) { + return false; + } + textureManager->Unload(it->second.id); + textureIds.erase(it->second.id); + textureAssets.erase(it); + textureDefs.erase(key); + return true; +} + +bool AssetManager::UnloadShader(const std::string& key) +{ + if (!shaderManager) { + log_warn("AssetManager: shader manager not set"); + return false; + } + auto it = shaderAssets.find(key); + if (it == shaderAssets.end()) { + return false; + } + shaderManager->Unload(it->second.id); + shaderIds.erase(it->second.id); + shaderAssets.erase(it); + shaderDefs.erase(key); + return true; +} + +bool AssetManager::UnloadModel(const std::string& key) +{ + if (!modelManager) { + log_warn("AssetManager: model manager not set"); + return false; + } + auto it = modelAssets.find(key); + if (it == modelAssets.end()) { + return false; + } + modelManager->Unload(it->second.id); + modelIds.erase(it->second.id); + modelAssets.erase(it); + modelDefs.erase(key); + return true; +} + +bool AssetManager::UnloadMaterial(const std::string& key) +{ + if (!materialManager) { + log_warn("AssetManager: material manager not set"); + return false; + } + auto it = materialAssets.find(key); + if (it == materialAssets.end()) { + return false; + } + materialManager->Remove(it->second.id); + materialIds.erase(it->second.id); + materialAssets.erase(it); + materialDefs.erase(key); + return true; +} + +unsigned int AssetManager::GetTextureId(const std::string& key) const +{ + auto it = textureAssets.find(key); + return (it != textureAssets.end()) ? it->second.id : 0; +} + +unsigned int AssetManager::GetShaderId(const std::string& key) const +{ + auto it = shaderAssets.find(key); + return (it != shaderAssets.end()) ? it->second.id : 0; +} + +unsigned int AssetManager::GetModelId(const std::string& key) const +{ + auto it = modelAssets.find(key); + return (it != modelAssets.end()) ? it->second.id : 0; +} + +unsigned int AssetManager::GetMaterialId(const std::string& key) const +{ + auto it = materialAssets.find(key); + return (it != materialAssets.end()) ? it->second.id : 0; +} + +const std::string& AssetManager::GetTextureKey(unsigned int id) const +{ + return GetKeyForId(textureIds, id); +} + +const std::string& AssetManager::GetShaderKey(unsigned int id) const +{ + return GetKeyForId(shaderIds, id); +} + +const std::string& AssetManager::GetModelKey(unsigned int id) const +{ + return GetKeyForId(modelIds, id); +} + +const std::string& AssetManager::GetMaterialKey(unsigned int id) const +{ + return GetKeyForId(materialIds, id); +} + +std::vector AssetManager::ListTextureKeys() const +{ + return ListKeys(textureAssets); +} + +std::vector AssetManager::ListShaderKeys() const +{ + return ListKeys(shaderAssets); +} + +std::vector AssetManager::ListModelKeys() const +{ + return ListKeys(modelAssets); +} + +std::vector AssetManager::ListMaterialKeys() const +{ + return ListKeys(materialAssets); +} + +std::string AssetManager::GetTextureSource(const std::string& key) const +{ + auto it = textureAssets.find(key); + return (it != textureAssets.end()) ? it->second.source : std::string(); +} + +std::string AssetManager::GetShaderSource(const std::string& key) const +{ + auto it = shaderAssets.find(key); + return (it != shaderAssets.end()) ? it->second.source : std::string(); +} + +std::string AssetManager::GetModelSource(const std::string& key) const +{ + auto it = modelAssets.find(key); + return (it != modelAssets.end()) ? it->second.source : std::string(); +} + +std::string AssetManager::GetMaterialSource(const std::string& key) const +{ + auto it = materialAssets.find(key); + return (it != materialAssets.end()) ? it->second.source : std::string(); +} + +void AssetManager::Clear() +{ + textureAssets.clear(); + shaderAssets.clear(); + modelAssets.clear(); + materialAssets.clear(); + + textureDefs.clear(); + shaderDefs.clear(); + modelDefs.clear(); + materialDefs.clear(); + + textureIds.clear(); + shaderIds.clear(); + modelIds.clear(); + materialIds.clear(); +} + +const std::string& AssetManager::GetKeyForId(const std::unordered_map& map, unsigned int id) const +{ + static const std::string empty; + auto it = map.find(id); + return (it != map.end()) ? it->second : empty; +} + +std::vector AssetManager::ListKeys(const std::unordered_map& map) const +{ + std::vector keys; + keys.reserve(map.size()); + for (const auto& pair : map) { + keys.push_back(pair.first); + } + return keys; +} + +bool AssetManager::SaveRegistry(const std::string& path) const +{ + auto parentPos = path.find_last_of("/\\"); + if (parentPos != std::string::npos) { + PhysFSManager::EnsureDir(path.substr(0, parentPos)); + } + + std::ostringstream file; + file << "# Simian asset registry\n"; + file << "# Auto-generated - edits may be overwritten\n\n"; + file << std::setprecision(6); + + for (const auto& [key, def] : textureDefs) { + if (key.empty() || def.path.empty()) { + continue; + } + file << "[[texture]]\n"; + file << "key = \"" << EscapeStringAsset(key) << "\"\n"; + file << "path = \"" << EscapeStringAsset(def.path) << "\"\n\n"; + } + + for (const auto& [key, def] : shaderDefs) { + if (key.empty() || def.vsPath.empty() || def.fsPath.empty()) { + continue; + } + file << "[[shader]]\n"; + file << "key = \"" << EscapeStringAsset(key) << "\"\n"; + file << "vs = \"" << EscapeStringAsset(def.vsPath) << "\"\n"; + file << "fs = \"" << EscapeStringAsset(def.fsPath) << "\"\n\n"; + } + + for (const auto& [key, def] : modelDefs) { + if (key.empty()) { + continue; + } + if (def.type == ModelDefinition::Type::Mesh) { + continue; + } + + file << "[[model]]\n"; + file << "key = \"" << EscapeStringAsset(key) << "\"\n"; + switch (def.type) { + case ModelDefinition::Type::Path: + if (!def.path.empty()) { + file << "type = \"path\"\n"; + file << "path = \"" << EscapeStringAsset(def.path) << "\"\n\n"; + } + break; + case ModelDefinition::Type::Primitive: + if (!def.primitive.empty() && !def.params.empty()) { + file << "type = \"primitive\"\n"; + file << "primitive = \"" << EscapeStringAsset(def.primitive) << "\"\n"; + file << "params = "; + WriteFloatList(file, def.params); + file << "\n\n"; + } + break; + case ModelDefinition::Type::Heightmap: + if (!def.path.empty() && def.params.size() >= 3) { + file << "type = \"heightmap\"\n"; + file << "path = \"" << EscapeStringAsset(def.path) << "\"\n"; + file << "size = "; + WriteFloatList(file, def.params); + file << "\n\n"; + } + break; + case ModelDefinition::Type::Cubicmap: + if (!def.path.empty() && def.params.size() >= 3) { + file << "type = \"cubicmap\"\n"; + file << "path = \"" << EscapeStringAsset(def.path) << "\"\n"; + file << "size = "; + WriteFloatList(file, def.params); + file << "\n\n"; + } + break; + case ModelDefinition::Type::Mesh: + break; + } + } + + for (const auto& [key, def] : materialDefs) { + if (key.empty()) { + continue; + } + file << "[[material]]\n"; + file << "key = \"" << EscapeStringAsset(key) << "\"\n"; + if (def.hasShaderKey && !def.shaderKey.empty()) { + file << "shader_key = \"" << EscapeStringAsset(def.shaderKey) << "\"\n"; + } else if (def.hasShaderId && def.shaderId > 0) { + file << "shader_id = " << def.shaderId << "\n"; + } + std::vector albedo = { + static_cast(def.albedo.r), + static_cast(def.albedo.g), + static_cast(def.albedo.b), + static_cast(def.albedo.a) + }; + file << "albedo = "; + WriteFloatList(file, albedo); + file << "\n\n"; + } + + return PhysFSManager::WriteTextFile(path, file.str()); +} + +bool AssetManager::LoadRegistry(const std::string& path) +{ + std::string contents; + if (!PhysFSManager::ReadTextFile(path, contents)) { + log_warn("Asset registry not found: %s", path.c_str()); + return false; + } + + enum class Section { + None, + Texture, + Shader, + Model, + Material + }; + + Section section = Section::None; + std::string entryKey; + std::unordered_map fields; + + auto flushEntry = [&]() { + if (section == Section::None || entryKey.empty()) { + entryKey.clear(); + fields.clear(); + return; + } + + if (section == Section::Texture) { + auto it = fields.find("path"); + if (it != fields.end()) { + std::string pathValue; + if (ParseStringAsset(it->second, pathValue)) { + textureDefs[entryKey] = TextureDefinition{pathValue}; + if (GetTextureId(entryKey) == 0) { + LoadTexture(entryKey, pathValue); + } + } + } + } else if (section == Section::Shader) { + std::string vsPath; + std::string fsPath; + auto itVs = fields.find("vs"); + auto itFs = fields.find("fs"); + if (itVs != fields.end() && itFs != fields.end() && + ParseStringAsset(itVs->second, vsPath) && ParseStringAsset(itFs->second, fsPath)) { + shaderDefs[entryKey] = ShaderDefinition{vsPath, fsPath}; + if (GetShaderId(entryKey) == 0) { + LoadShader(entryKey, vsPath, fsPath); + } + } + } else if (section == Section::Model) { + std::string typeValue; + auto itType = fields.find("type"); + if (itType != fields.end() && ParseStringAsset(itType->second, typeValue)) { + if (typeValue == "path") { + std::string modelPath; + auto itPath = fields.find("path"); + if (itPath != fields.end() && ParseStringAsset(itPath->second, modelPath)) { + ModelDefinition def; + def.type = ModelDefinition::Type::Path; + def.path = modelPath; + modelDefs[entryKey] = def; + if (GetModelId(entryKey) == 0) { + LoadModel(entryKey, modelPath); + } + } + } else if (typeValue == "primitive") { + std::string primitive; + std::vector params; + auto itPrim = fields.find("primitive"); + auto itParams = fields.find("params"); + if (itPrim != fields.end() && itParams != fields.end() && + ParseStringAsset(itPrim->second, primitive) && ParseFloatList(itParams->second, params)) { + ModelDefinition def; + def.type = ModelDefinition::Type::Primitive; + def.primitive = primitive; + def.params = params; + modelDefs[entryKey] = def; + + if (GetModelId(entryKey) == 0) { + if (primitive == "cube" && params.size() >= 3) { + LoadCube(entryKey, params[0], params[1], params[2]); + } else if (primitive == "poly" && params.size() >= 2) { + LoadPoly(entryKey, static_cast(params[0]), params[1]); + } else if (primitive == "sphere" && params.size() >= 3) { + LoadSphere(entryKey, params[0], static_cast(params[1]), static_cast(params[2])); + } else if (primitive == "hemisphere" && params.size() >= 3) { + LoadHemiSphere(entryKey, params[0], static_cast(params[1]), static_cast(params[2])); + } else if (primitive == "plane" && params.size() >= 4) { + LoadPlane(entryKey, params[0], params[1], static_cast(params[2]), static_cast(params[3])); + } else if (primitive == "cylinder" && params.size() >= 3) { + LoadCylinder(entryKey, params[0], params[1], static_cast(params[2])); + } else if (primitive == "cone" && params.size() >= 3) { + LoadCone(entryKey, params[0], params[1], static_cast(params[2])); + } else if (primitive == "torus" && params.size() >= 4) { + LoadTorus(entryKey, params[0], params[1], static_cast(params[2]), static_cast(params[3])); + } else if (primitive == "knot" && params.size() >= 4) { + LoadKnot(entryKey, params[0], params[1], static_cast(params[2]), static_cast(params[3])); + } + } + } + } else if (typeValue == "heightmap" || typeValue == "cubicmap") { + std::string mapPath; + std::vector sizeValues; + auto itPath = fields.find("path"); + auto itSize = fields.find("size"); + if (itPath != fields.end() && itSize != fields.end() && + ParseStringAsset(itPath->second, mapPath) && ParseFloatList(itSize->second, sizeValues) && sizeValues.size() >= 3) { + ModelDefinition def; + def.type = (typeValue == "heightmap") ? ModelDefinition::Type::Heightmap : ModelDefinition::Type::Cubicmap; + def.path = mapPath; + def.params = sizeValues; + modelDefs[entryKey] = def; + if (GetModelId(entryKey) == 0) { + if (typeValue == "heightmap") { + LoadHeightmap(entryKey, mapPath, sizeValues[0], sizeValues[1], sizeValues[2]); + } else { + LoadCubicmap(entryKey, mapPath, sizeValues[0], sizeValues[1], sizeValues[2]); + } + } + } + } + } + } else if (section == Section::Material) { + MaterialDefinition def; + std::vector albedoVals; + + auto itShaderKey = fields.find("shader_key"); + if (itShaderKey != fields.end()) { + std::string shaderKey; + if (ParseStringAsset(itShaderKey->second, shaderKey)) { + def.shaderKey = shaderKey; + def.hasShaderKey = true; + } + } + + auto itShaderId = fields.find("shader_id"); + if (!def.hasShaderKey && itShaderId != fields.end()) { + try { + def.shaderId = static_cast(std::stoul(TrimAsset(itShaderId->second), nullptr, 0)); + def.hasShaderId = def.shaderId > 0; + } catch (...) { + } + } + + auto itAlbedo = fields.find("albedo"); + if (itAlbedo != fields.end() && ParseFloatList(itAlbedo->second, albedoVals) && albedoVals.size() >= 4) { + def.albedo = { + static_cast(std::clamp(albedoVals[0], 0.0f, 255.0f)), + static_cast(std::clamp(albedoVals[1], 0.0f, 255.0f)), + static_cast(std::clamp(albedoVals[2], 0.0f, 255.0f)), + static_cast(std::clamp(albedoVals[3], 0.0f, 255.0f)) + }; + } + + materialDefs[entryKey] = def; + + if (GetMaterialId(entryKey) == 0) { + unsigned int shaderId = 0; + if (def.hasShaderKey) { + shaderId = GetShaderId(def.shaderKey); + } else if (def.hasShaderId) { + shaderId = def.shaderId; + } + CreateMaterial(entryKey, shaderId, def.albedo); + materialDefs[entryKey] = def; + } + } + + entryKey.clear(); + fields.clear(); + }; + + std::string line; + std::istringstream file(contents); + while (std::getline(file, line)) { + line = StripCommentAsset(line); + line = TrimAsset(line); + if (line.empty()) { + continue; + } + + if (line == "[[texture]]") { + flushEntry(); + section = Section::Texture; + continue; + } + if (line == "[[shader]]") { + flushEntry(); + section = Section::Shader; + continue; + } + if (line == "[[model]]") { + flushEntry(); + section = Section::Model; + continue; + } + if (line == "[[material]]") { + flushEntry(); + section = Section::Material; + continue; + } + + size_t eq = line.find('='); + if (eq == std::string::npos) { + continue; + } + + std::string key = TrimAsset(line.substr(0, eq)); + std::string value = TrimAsset(line.substr(eq + 1)); + if (key == "key") { + ParseStringAsset(value, entryKey); + } else { + fields[key] = value; + } + } + + flushEntry(); + return true; +} diff --git a/src/GuiManager.cpp b/src/GuiManager.cpp deleted file mode 100644 index 3823a64..0000000 --- a/src/GuiManager.cpp +++ /dev/null @@ -1,229 +0,0 @@ -#include "GuiManager.h" -#include "raylib.h" -#include // Add this to fix the incomplete type error -#include -#include -#include "log.h" -#include "Application.h" -#include // Add this for cross-platform file handling - -GuiManager::GuiManager() {} - -GuiManager::~GuiManager() {} - -void GuiManager::Initialize(Application *application) -{ - app = application; - rlImGuiSetup(true); - ImGuiIO &io = ImGui::GetIO(); - io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; - io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; - io.Fonts->AddFontDefault(); - - float baseFontSize = 16.0f; - float iconFontSize = baseFontSize * 2.0f / 3.0f; - - static constexpr ImWchar iconsRanges[] = {ICON_MIN_FA, ICON_MAX_16_FA, 0}; - ImFontConfig iconsConfig; - iconsConfig.MergeMode = true; - iconsConfig.PixelSnapH = true; - iconsConfig.GlyphMinAdvanceX = iconFontSize; - io.Fonts->AddFontFromMemoryCompressedTTF(fa_solid_900_compressed_data, fa_solid_900_compressed_size, iconFontSize, &iconsConfig, iconsRanges); - SetTheme(); - - logWindow.Initialize(); // Initialize the LogWindow - log_trace("GuiManager::Initialize - Started"); -} - -void GuiManager::Render(RenderTexture2D &renderTexture) -{ - rlImGuiBegin(); - SetupDockspace(renderTexture); - - RenderErrorBanner(); - RenderNotifications(); - logWindow.Render(showLogWindow); // Render the LogWindow - rlImGuiEnd(); - - ImGui::UpdatePlatformWindows(); - ImGui::RenderPlatformWindowsDefault(); -} - -void GuiManager::Shutdown() -{ - rlImGuiShutdown(); -} - -void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) -{ - static bool dockspaceOpen = true; - static bool opt_fullscreen = true; - static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; - - ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; - if (opt_fullscreen) - { - ImGuiViewport *viewport = ImGui::GetMainViewport(); - ImGui::SetNextWindowPos(viewport->WorkPos); - ImGui::SetNextWindowSize(viewport->WorkSize); - ImGui::SetNextWindowViewport(viewport->ID); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); - window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; - window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; - } - - ImGui::Begin("DockSpaceHost", &dockspaceOpen, window_flags); - if (opt_fullscreen) - ImGui::PopStyleVar(2); - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("Game")) - { - if (ImGui::MenuItem("Quit", "Ctrl + Q")) { - // Handle quit here - app->Shutdown(); - } - ImGui::EndMenu(); - } - if (ImGui::BeginMenu("View")) - { - ImGui::MenuItem("Log Viewer", "Ctrl + L", &showLogWindow); - ImGui::EndMenu(); - } - ImGui::EndMenuBar(); - } - - if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_L)) - { - showLogWindow = !showLogWindow; - } - if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Q)) - { - app->Shutdown(); - } - ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); - ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); - - // Add a Game Window - ImGui::Begin("Game Window"); - - // Render Raylib content to the Game Window - rlImGuiImageRenderTexture(&renderTexture); - - ImGui::End(); - ImGui::End(); -} - -void GuiManager::RenderErrorBanner() -{ - if (!app || !app->HasScriptCompilationError()) - return; - - ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); - ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, 0.0f)); - ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | - ImGuiWindowFlags_NoMove | - ImGuiWindowFlags_NoSavedSettings; - ImGui::Begin("PersistentBanner", nullptr, flags); - ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), ICON_FA_TRIANGLE_EXCLAMATION " Script Compilation Error - running last working version"); - ImGui::End(); -} - -void GuiManager::RenderNotifications() -{ - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.f); - ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f); - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.10f, 0.10f, 0.10f, 1.00f)); - - ImGui::RenderNotifications(); - - ImGui::PopStyleVar(2); - ImGui::PopStyleColor(1); -} - -void GuiManager::SetTheme() -{ - - ImVec4 *colors = ImGui::GetStyle().Colors; - colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); - colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); - colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f); - colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_PopupBg] = ImVec4(0.19f, 0.19f, 0.19f, 0.92f); - colors[ImGuiCol_Border] = ImVec4(0.19f, 0.19f, 0.19f, 0.29f); - colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.24f); - colors[ImGuiCol_FrameBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); - colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f); - colors[ImGuiCol_FrameBgActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); - colors[ImGuiCol_TitleBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_TitleBgActive] = ImVec4(0.06f, 0.06f, 0.06f, 1.00f); - colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); - colors[ImGuiCol_ScrollbarBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); - colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); - colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.54f); - colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); - colors[ImGuiCol_CheckMark] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); - colors[ImGuiCol_SliderGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); - colors[ImGuiCol_SliderGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); - colors[ImGuiCol_Button] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); - colors[ImGuiCol_ButtonHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f); - colors[ImGuiCol_ButtonActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); - colors[ImGuiCol_Header] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); - colors[ImGuiCol_HeaderHovered] = ImVec4(0.00f, 0.00f, 0.00f, 0.36f); - colors[ImGuiCol_HeaderActive] = ImVec4(0.20f, 0.22f, 0.23f, 0.33f); - colors[ImGuiCol_Separator] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); - colors[ImGuiCol_SeparatorHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); - colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); - colors[ImGuiCol_ResizeGrip] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); - colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); - colors[ImGuiCol_ResizeGripActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); - colors[ImGuiCol_Tab] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); - colors[ImGuiCol_TabHovered] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); - colors[ImGuiCol_TabActive] = ImVec4(0.20f, 0.20f, 0.20f, 0.36f); - colors[ImGuiCol_TabUnfocused] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); - colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); - // colors[ImGuiCol_DockingPreview] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); - // colors[ImGuiCol_DockingEmptyBg] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_TableHeaderBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); - colors[ImGuiCol_TableBorderStrong] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); - colors[ImGuiCol_TableBorderLight] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); - colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); - colors[ImGuiCol_TextSelectedBg] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); - colors[ImGuiCol_DragDropTarget] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); - colors[ImGuiCol_NavHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); - colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 0.70f); - colors[ImGuiCol_NavWindowingDimBg] = ImVec4(1.00f, 0.00f, 0.00f, 0.20f); - colors[ImGuiCol_ModalWindowDimBg] = ImVec4(1.00f, 0.00f, 0.00f, 0.35f); - colors[ImGuiCol_CheckMark] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); - - ImGuiStyle &style = ImGui::GetStyle(); - style.WindowPadding = ImVec2(8.00f, 8.00f); - style.FramePadding = ImVec2(5.00f, 2.00f); - style.CellPadding = ImVec2(6.00f, 6.00f); - style.ItemSpacing = ImVec2(6.00f, 6.00f); - style.ItemInnerSpacing = ImVec2(6.00f, 6.00f); - style.TouchExtraPadding = ImVec2(0.00f, 0.00f); - style.IndentSpacing = 25; - style.ScrollbarSize = 15; - style.GrabMinSize = 10; - style.WindowBorderSize = 1; - style.ChildBorderSize = 1; - style.PopupBorderSize = 1; - style.FrameBorderSize = 1; - style.TabBorderSize = 1; - style.WindowRounding = 7; - style.ChildRounding = 4; - style.FrameRounding = 3; - style.PopupRounding = 4; - style.ScrollbarRounding = 9; - style.GrabRounding = 3; - style.LogSliderDeadzone = 4; - style.TabRounding = 4; -} diff --git a/src/HotReload.cpp b/src/HotReload.cpp index ca96ae9..57ad21a 100644 --- a/src/HotReload.cpp +++ b/src/HotReload.cpp @@ -1,8 +1,7 @@ #include "HotReload.h" +#include "PhysFSManager.h" #include -namespace fs = std::filesystem; - HotReload::HotReload(const std::string& pathToWatch) : path(pathToWatch), lastWriteTime(0) { UpdateLastWriteTime(); } @@ -21,36 +20,32 @@ void HotReload::UpdateLastWriteTime() { } std::time_t HotReload::GetFileWriteTime(const std::string& filename) { - try { - auto ftime = fs::last_write_time(filename); - auto sctp = std::chrono::time_point_cast( - ftime - fs::file_time_type::clock::now() + std::chrono::system_clock::now()); - return std::chrono::system_clock::to_time_t(sctp); - } catch (const std::exception&) { - return 0; - } + return static_cast(PhysFSManager::GetModTime(filename)); } std::time_t HotReload::GetLatestWriteTimeInPath() { - try { - fs::path p(path); - if (fs::is_regular_file(p)) { - return GetFileWriteTime(path); - } - - // If it's a directory, scan files (non-recursively) and return the - // newest write time found. - if (fs::is_directory(p)) { - std::time_t newest = 0; - for (auto &entry : fs::directory_iterator(p)) { - if (!fs::is_regular_file(entry.path())) continue; - std::time_t t = GetFileWriteTime(entry.path().string()); - if (t > newest) newest = t; - } - return newest; - } - } catch (const std::exception&) { - // fall through + if (PhysFSManager::Exists(path) && !PhysFSManager::IsDirectory(path)) { + return GetFileWriteTime(path); } + + if (PhysFSManager::IsDirectory(path)) { + std::time_t newest = 0; + auto entries = PhysFSManager::ListFiles(path); + for (const auto& entry : entries) { + if (entry == "." || entry == "..") continue; + std::string child = path; + if (!child.empty() && child.back() != '/') { + child.push_back('/'); + } + child += entry; + if (PhysFSManager::IsDirectory(child)) { + continue; + } + std::time_t t = GetFileWriteTime(child); + if (t > newest) newest = t; + } + return newest; + } + return 0; -} \ No newline at end of file +} diff --git a/src/InputManager.cpp b/src/InputManager.cpp new file mode 100644 index 0000000..e6c94b1 --- /dev/null +++ b/src/InputManager.cpp @@ -0,0 +1,245 @@ +#include "InputManager.h" +#include "log/log.h" +#include +#include +#include + +InputManager::InputManager() { +} + +InputManager::~InputManager() { +} + +void InputManager::RegisterAction(const std::string& actionName) { + if (actions.find(actionName) == actions.end()) { + actions[actionName] = std::vector(); + log_debug("Registered input action: %s", actionName.c_str()); + } +} + +void InputManager::UnregisterAction(const std::string& actionName) { + actions.erase(actionName); +} + +bool InputManager::HasAction(const std::string& actionName) const { + return actions.find(actionName) != actions.end(); +} + +void InputManager::BindKeyboard(const std::string& actionName, int keyCode) { + RegisterAction(actionName); + actions[actionName].push_back(InputBinding(InputType::Keyboard, keyCode)); + log_debug("Bound key %d to action: %s", keyCode, actionName.c_str()); +} + +void InputManager::BindMouseButton(const std::string& actionName, int mouseButton) { + RegisterAction(actionName); + actions[actionName].push_back(InputBinding(InputType::MouseButton, mouseButton)); +} + +void InputManager::BindGamepadButton(const std::string& actionName, int gamepadId, int button) { + RegisterAction(actionName); + actions[actionName].push_back(InputBinding(InputType::GamepadButton, button, gamepadId)); +} + +void InputManager::BindGamepadAxis(const std::string& actionName, int gamepadId, int axis, float threshold, bool negative) { + RegisterAction(actionName); + InputBinding binding(InputType::GamepadAxis, axis, gamepadId); + binding.axisThreshold = threshold; + binding.negative = negative; + actions[actionName].push_back(binding); +} + +void InputManager::ClearBindings(const std::string& actionName) { + if (HasAction(actionName)) { + actions[actionName].clear(); + } +} + +void InputManager::ClearAllActions() { + actions.clear(); +} + +bool InputManager::IsActionPressed(const std::string& actionName) const { + auto it = actions.find(actionName); + if (it == actions.end()) return false; + + for (const auto& binding : it->second) { + if (IsBindingPressed(binding)) { + return true; + } + } + return false; +} + +bool InputManager::IsActionDown(const std::string& actionName) const { + auto it = actions.find(actionName); + if (it == actions.end()) return false; + + for (const auto& binding : it->second) { + if (IsBindingDown(binding)) { + return true; + } + } + return false; +} + +bool InputManager::IsActionReleased(const std::string& actionName) const { + auto it = actions.find(actionName); + if (it == actions.end()) return false; + + for (const auto& binding : it->second) { + if (IsBindingReleased(binding)) { + return true; + } + } + return false; +} + +float InputManager::GetActionStrength(const std::string& actionName) const { + auto it = actions.find(actionName); + if (it == actions.end()) return 0.0f; + + float maxStrength = 0.0f; + for (const auto& binding : it->second) { + float strength = GetBindingStrength(binding); + if (std::abs(strength) > std::abs(maxStrength)) { + maxStrength = strength; + } + } + return maxStrength; +} + +bool InputManager::IsBindingPressed(const InputBinding& binding) const { + switch (binding.type) { + case InputType::Keyboard: + return IsKeyPressed(binding.code); + case InputType::MouseButton: + return IsMouseButtonPressed(binding.code); + case InputType::GamepadButton: + if (IsGamepadAvailable(binding.gamepadId)) { + return IsGamepadButtonPressed(binding.gamepadId, binding.code); + } + return false; + case InputType::GamepadAxis: { + // Axis "pressed" is harder to detect - simplified to just checking threshold + if (IsGamepadAvailable(binding.gamepadId)) { + float value = GetGamepadAxisMovement(binding.gamepadId, binding.code); + return binding.negative ? value < -binding.axisThreshold : value > binding.axisThreshold; + } + return false; + } + } + return false; +} + +bool InputManager::IsBindingDown(const InputBinding& binding) const { + switch (binding.type) { + case InputType::Keyboard: + return IsKeyDown(binding.code); + case InputType::MouseButton: + return IsMouseButtonDown(binding.code); + case InputType::GamepadButton: + if (IsGamepadAvailable(binding.gamepadId)) { + return IsGamepadButtonDown(binding.gamepadId, binding.code); + } + return false; + case InputType::GamepadAxis: + if (IsGamepadAvailable(binding.gamepadId)) { + float value = GetGamepadAxisMovement(binding.gamepadId, binding.code); + return binding.negative ? value < -binding.axisThreshold : value > binding.axisThreshold; + } + return false; + } + return false; +} + +bool InputManager::IsBindingReleased(const InputBinding& binding) const { + switch (binding.type) { + case InputType::Keyboard: + return IsKeyReleased(binding.code); + case InputType::MouseButton: + return IsMouseButtonReleased(binding.code); + case InputType::GamepadButton: + if (IsGamepadAvailable(binding.gamepadId)) { + return IsGamepadButtonReleased(binding.gamepadId, binding.code); + } + return false; + case InputType::GamepadAxis: + return false; // Axes don't have a clear "released" state + } + return false; +} + +float InputManager::GetBindingStrength(const InputBinding& binding) const { + switch (binding.type) { + case InputType::Keyboard: + return IsKeyDown(binding.code) ? 1.0f : 0.0f; + case InputType::MouseButton: + return IsMouseButtonDown(binding.code) ? 1.0f : 0.0f; + case InputType::GamepadButton: + if (IsGamepadAvailable(binding.gamepadId)) { + return IsGamepadButtonDown(binding.gamepadId, binding.code) ? 1.0f : 0.0f; + } + return 0.0f; + case InputType::GamepadAxis: + if (IsGamepadAvailable(binding.gamepadId)) { + float value = GetGamepadAxisMovement(binding.gamepadId, binding.code); + return binding.negative ? -value : value; + } + return 0.0f; + } + return 0.0f; +} + +void InputManager::SetupDefaultBindings() { + // Movement + BindKeyboard("move_up", KEY_W); + BindKeyboard("move_up", KEY_UP); + BindKeyboard("move_down", KEY_S); + BindKeyboard("move_down", KEY_DOWN); + BindKeyboard("move_left", KEY_A); + BindKeyboard("move_left", KEY_LEFT); + BindKeyboard("move_right", KEY_D); + BindKeyboard("move_right", KEY_RIGHT); + + // Actions + BindKeyboard("jump", KEY_SPACE); + BindKeyboard("interact", KEY_E); + BindKeyboard("attack", KEY_LEFT_CONTROL); + BindMouseButton("attack", MOUSE_BUTTON_LEFT); + + // Gamepad (if available) + BindGamepadButton("jump", 0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN); + BindGamepadButton("interact", 0, GAMEPAD_BUTTON_RIGHT_FACE_RIGHT); + BindGamepadAxis("move_left", 0, GAMEPAD_AXIS_LEFT_X, 0.3f, true); + BindGamepadAxis("move_right", 0, GAMEPAD_AXIS_LEFT_X, 0.3f, false); + + log_info("Input manager: Default bindings configured"); +} + +std::vector InputManager::GetAllActions() const { + std::vector result; + for (const auto& pair : actions) { + result.push_back(pair.first); + } + return result; +} + +const std::vector& InputManager::GetBindings(const std::string& actionName) const { + static std::vector empty; + auto it = actions.find(actionName); + if (it != actions.end()) { + return it->second; + } + return empty; +} + +void InputManager::LoadFromFile(const std::string& filepath) { + // TODO: Implement config file loading (JSON or simple text format) + log_info("Input config loading not yet implemented: %s", filepath.c_str()); +} + +void InputManager::SaveToFile(const std::string& filepath) const { + // TODO: Implement config file saving + log_info("Input config saving not yet implemented: %s", filepath.c_str()); +} diff --git a/src/MaterialManager.cpp b/src/MaterialManager.cpp new file mode 100644 index 0000000..ebcaf3f --- /dev/null +++ b/src/MaterialManager.cpp @@ -0,0 +1,76 @@ +#include "MaterialManager.h" +#include "log/log.h" + +MaterialManager::MaterialManager() : nextId(1), cleared(false) +{ +} + +MaterialManager::~MaterialManager() +{ + // Only clear if not already done + if (!cleared) { + materials.clear(); + } +} + +unsigned int MaterialManager::Create(unsigned int shaderId, Color albedo) +{ + unsigned int id = nextId++; + + MaterialData data; + data.shaderId = shaderId; + data.albedo = albedo; + + materials[id] = data; + log_info("Created material id=%u with shader=%u", id, shaderId); + + return id; +} + +void MaterialManager::Remove(unsigned int id) +{ + auto it = materials.find(id); + if (it != materials.end()) + { + materials.erase(it); + log_info("Removed material id=%u", id); + } +} + +MaterialData* MaterialManager::Get(unsigned int id) +{ + auto it = materials.find(id); + if (it != materials.end()) + { + return &it->second; + } + return nullptr; +} + +void MaterialManager::SetShader(unsigned int id, unsigned int shaderId) +{ + auto it = materials.find(id); + if (it != materials.end()) + { + it->second.shaderId = shaderId; + log_info("Updated material id=%u shader to %u", id, shaderId); + } +} + +void MaterialManager::SetAlbedo(unsigned int id, Color albedo) +{ + auto it = materials.find(id); + if (it != materials.end()) + { + it->second.albedo = albedo; + } +} + +void MaterialManager::Clear() +{ + if (!cleared) { + materials.clear(); + cleared = true; + log_info("Cleared all materials"); + } +} diff --git a/src/ModelManager.cpp b/src/ModelManager.cpp new file mode 100644 index 0000000..fd329a1 --- /dev/null +++ b/src/ModelManager.cpp @@ -0,0 +1,139 @@ +#include "ModelManager.h" +#include "log/log.h" + +ModelManager::ModelManager() : nextId(1), unloaded(false) +{ +} + +ModelManager::~ModelManager() +{ + // Only unload if not already done + if (!unloaded) { + for (auto& pair : models) { + UnloadModel(pair.second); + } + models.clear(); + modelSources.clear(); + } +} + +unsigned int ModelManager::Load(const std::string& path) +{ + Model model = LoadModel(path.c_str()); + + if (model.meshCount == 0) + { + log_warn("Failed to load model: %s", path.c_str()); + return 0; + } + + unsigned int id = nextId++; + models[id] = model; + ModelSource source; + source.type = ModelSourceType::Path; + source.path = path; + modelSources[id] = source; + log_info("Loaded model id=%u from %s", id, path.c_str()); + + return id; +} + +unsigned int ModelManager::LoadFromMesh(Mesh mesh) +{ + Model model = LoadModelFromMesh(mesh); + + unsigned int id = nextId++; + models[id] = model; + log_info("Loaded model id=%u from mesh", id); + + return id; +} + +unsigned int ModelManager::LoadCube(float width, float height, float length) +{ + Mesh mesh = GenMeshCube(width, height, length); + unsigned int id = LoadFromMesh(mesh); + if (id != 0) { + ModelSource source; + source.type = ModelSourceType::Cube; + source.size = {width, height, length}; + modelSources[id] = source; + } + return id; +} + +unsigned int ModelManager::LoadSphere(float radius, int rings, int slices) +{ + Mesh mesh = GenMeshSphere(radius, rings, slices); + unsigned int id = LoadFromMesh(mesh); + if (id != 0) { + ModelSource source; + source.type = ModelSourceType::Sphere; + source.radius = radius; + source.rings = rings; + source.slices = slices; + modelSources[id] = source; + } + return id; +} + +unsigned int ModelManager::LoadPlane(float width, float length, int resX, int resZ) +{ + Mesh mesh = GenMeshPlane(width, length, resX, resZ); + unsigned int id = LoadFromMesh(mesh); + if (id != 0) { + ModelSource source; + source.type = ModelSourceType::Plane; + source.size = {width, 1.0f, length}; + source.rings = resX; + source.slices = resZ; + modelSources[id] = source; + } + return id; +} + +void ModelManager::Unload(unsigned int id) +{ + auto it = models.find(id); + if (it != models.end()) + { + UnloadModel(it->second); + models.erase(it); + modelSources.erase(id); + log_info("Unloaded model id=%u", id); + } +} + +Model* ModelManager::Get(unsigned int id) +{ + auto it = models.find(id); + if (it != models.end()) + { + return &it->second; + } + return nullptr; +} + +const ModelSource* ModelManager::GetSource(unsigned int id) const +{ + auto it = modelSources.find(id); + if (it != modelSources.end()) + { + return &it->second; + } + return nullptr; +} + +void ModelManager::UnloadAll() +{ + if (!unloaded) { + for (auto& pair : models) + { + UnloadModel(pair.second); + } + models.clear(); + modelSources.clear(); + unloaded = true; + log_info("Unloaded all models"); + } +} diff --git a/src/PhysFSManager.cpp b/src/PhysFSManager.cpp new file mode 100644 index 0000000..4b50d5a --- /dev/null +++ b/src/PhysFSManager.cpp @@ -0,0 +1,250 @@ +#include "PhysFSManager.h" +#include "raylib.h" +#include "physfs.h" +#include "log/log.h" +#include +#include +#include + +namespace PhysFSManager { + +static bool g_initialized = false; + +static unsigned char* RaylibLoadFileDataCallback(const char* fileName, int* dataSize) { + if (!fileName || !dataSize) { + return nullptr; + } + + PHYSFS_File* file = PHYSFS_openRead(fileName); + if (!file) { + *dataSize = 0; + return nullptr; + } + + PHYSFS_sint64 length = PHYSFS_fileLength(file); + if (length <= 0 || length > static_cast(INT32_MAX)) { + PHYSFS_close(file); + *dataSize = 0; + return nullptr; + } + + unsigned char* data = static_cast(MemAlloc(static_cast(length))); + if (!data) { + PHYSFS_close(file); + *dataSize = 0; + return nullptr; + } + + PHYSFS_sint64 read = PHYSFS_readBytes(file, data, length); + PHYSFS_close(file); + if (read != length) { + MemFree(data); + *dataSize = 0; + return nullptr; + } + + *dataSize = static_cast(length); + return data; +} + +static bool RaylibSaveFileDataCallback(const char* fileName, void* data, int dataSize) { + if (!fileName || !data || dataSize <= 0) { + return false; + } + + PHYSFS_File* file = PHYSFS_openWrite(fileName); + if (!file) { + return false; + } + + PHYSFS_sint64 written = PHYSFS_writeBytes(file, data, dataSize); + PHYSFS_close(file); + return written == dataSize; +} + +bool Initialize(const char* argv0) { + if (g_initialized) { + return true; + } + + if (!PHYSFS_init(argv0)) { + log_error("PhysFS init failed: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; + } + + PHYSFS_permitSymbolicLinks(0); + + // Configure raylib to use PhysFS for all file reads/writes. + SetLoadFileDataCallback(RaylibLoadFileDataCallback); + SetSaveFileDataCallback(RaylibSaveFileDataCallback); + + g_initialized = true; + return true; +} + +bool IsInitialized() { + return g_initialized; +} + +void Shutdown() { + if (!g_initialized) { + return; + } + + ClearSearchPath(); + SetLoadFileDataCallback(nullptr); + SetSaveFileDataCallback(nullptr); + PHYSFS_deinit(); + g_initialized = false; +} + +bool MountPath(const std::string& path, const std::string& mountPoint, bool append) { + const char* mount = mountPoint.empty() ? nullptr : mountPoint.c_str(); + if (!PHYSFS_mount(path.c_str(), mount, append ? 1 : 0)) { + log_warn("PhysFS mount failed: %s -> %s (%s)", path.c_str(), mountPoint.c_str(), + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; + } + return true; +} + +bool UnmountPath(const std::string& path) { + if (!PHYSFS_unmount(path.c_str())) { + return false; + } + return true; +} + +void ClearSearchPath() { + char** paths = PHYSFS_getSearchPath(); + if (!paths) { + return; + } + + for (char** i = paths; *i != nullptr; ++i) { + PHYSFS_unmount(*i); + } + + PHYSFS_freeList(paths); +} + +bool SetWriteDir(const std::string& path) { + if (!PHYSFS_setWriteDir(path.c_str())) { + log_warn("PhysFS set write dir failed: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); + return false; + } + return true; +} + +bool EnsureDir(const std::string& path) { + if (path.empty()) { + return true; + } + + std::string normalized = path; + std::replace(normalized.begin(), normalized.end(), '\\', '/'); + + size_t start = 0; + while (start < normalized.size()) { + size_t slash = normalized.find('/', start); + std::string segment = normalized.substr(0, slash); + if (!segment.empty() && !IsDirectory(segment)) { + if (!PHYSFS_mkdir(segment.c_str())) { + return false; + } + } + if (slash == std::string::npos) { + break; + } + start = slash + 1; + } + + return true; +} + +bool Exists(const std::string& path) { + return PHYSFS_exists(path.c_str()) != 0; +} + +bool IsDirectory(const std::string& path) { + return PHYSFS_isDirectory(path.c_str()) != 0; +} + +std::string GetRealDir(const std::string& path) { + const char* dir = PHYSFS_getRealDir(path.c_str()); + return dir ? std::string(dir) : std::string(); +} + +std::int64_t GetModTime(const std::string& path) { + PHYSFS_Stat stat{}; + if (!PHYSFS_stat(path.c_str(), &stat)) { + return 0; + } + return static_cast(stat.modtime); +} + +std::vector ListFiles(const std::string& path) { + std::vector result; + char** files = PHYSFS_enumerateFiles(path.c_str()); + if (!files) { + return result; + } + + for (char** i = files; *i != nullptr; ++i) { + result.emplace_back(*i); + } + + PHYSFS_freeList(files); + return result; +} + +bool ReadTextFile(const std::string& path, std::string& out) { + out.clear(); + PHYSFS_File* file = PHYSFS_openRead(path.c_str()); + if (!file) { + return false; + } + + PHYSFS_sint64 length = PHYSFS_fileLength(file); + if (length < 0) { + PHYSFS_close(file); + return false; + } + + out.resize(static_cast(length)); + PHYSFS_sint64 read = PHYSFS_readBytes(file, out.data(), length); + PHYSFS_close(file); + if (read != length) { + out.clear(); + return false; + } + + return true; +} + +bool WriteTextFile(const std::string& path, const std::string& contents) { + PHYSFS_File* file = PHYSFS_openWrite(path.c_str()); + if (!file) { + return false; + } + + PHYSFS_sint64 written = PHYSFS_writeBytes(file, contents.data(), contents.size()); + PHYSFS_close(file); + return written == static_cast(contents.size()); +} + +PHYSFS_File* OpenRead(const std::string& path) { + return PHYSFS_openRead(path.c_str()); +} + +PHYSFS_File* OpenWrite(const std::string& path) { + return PHYSFS_openWrite(path.c_str()); +} + +void Close(PHYSFS_File* file) { + if (file) { + PHYSFS_close(file); + } +} + +} // namespace PhysFSManager diff --git a/src/SceneGraph.cpp b/src/SceneGraph.cpp new file mode 100644 index 0000000..7b80929 --- /dev/null +++ b/src/SceneGraph.cpp @@ -0,0 +1,145 @@ +#include "SceneGraph.h" +#include "ECSComponents.h" +#include "raymath.h" +#include "entt.hpp" + +static Matrix BuildLocalMatrix(const ECSTransform& local) { + Matrix scale = MatrixScale(local.scale.x, local.scale.y, local.scale.z); + Matrix rotation = MatrixRotateXYZ(local.rotation); + Matrix translation = MatrixTranslate(local.position.x, local.position.y, local.position.z); + return MatrixMultiply(MatrixMultiply(translation, rotation), scale); +} + +static Vector3 ExtractScale(const Matrix& matrix) { + Vector3 scale; + scale.x = sqrtf(matrix.m0 * matrix.m0 + matrix.m1 * matrix.m1 + matrix.m2 * matrix.m2); + scale.y = sqrtf(matrix.m4 * matrix.m4 + matrix.m5 * matrix.m5 + matrix.m6 * matrix.m6); + scale.z = sqrtf(matrix.m8 * matrix.m8 + matrix.m9 * matrix.m9 + matrix.m10 * matrix.m10); + return scale; +} + +static WorldTransform ComputeWorldFromParent(ECSTransform& local, const WorldTransform* parentWorld) { + local.localMatrix = BuildLocalMatrix(local); + + WorldTransform world; + if (!parentWorld) { + world.worldMatrix = local.localMatrix; + world.position = local.position; + world.rotation = local.rotation; + world.scale = local.scale; + return world; + } + + world.worldMatrix = MatrixMultiply(parentWorld->worldMatrix, local.localMatrix); + world.position = {world.worldMatrix.m12, world.worldMatrix.m13, world.worldMatrix.m14}; + world.scale = ExtractScale(world.worldMatrix); + world.rotation = QuaternionToEuler(QuaternionFromMatrix(world.worldMatrix)); + return world; +} + +void AttachChild(entt::registry& registry, entt::entity child, entt::entity parent) { + if (child == parent) return; + + DetachFromParent(registry, child); + + auto& childHierarchy = registry.get_or_emplace(child); + auto& parentHierarchy = registry.get_or_emplace(parent); + + childHierarchy.parent = parent; + childHierarchy.prev = entt::null; + childHierarchy.next = parentHierarchy.first; + + if (parentHierarchy.first != entt::null) { + registry.get_or_emplace(parentHierarchy.first).prev = child; + } + + parentHierarchy.first = child; +} + +void DetachFromParent(entt::registry& registry, entt::entity child) { + auto* childHierarchy = registry.try_get(child); + if (!childHierarchy || childHierarchy->parent == entt::null) { + return; + } + + entt::entity parent = childHierarchy->parent; + auto& parentHierarchy = registry.get_or_emplace(parent); + + if (parentHierarchy.first == child) { + parentHierarchy.first = childHierarchy->next; + } + + if (childHierarchy->prev != entt::null) { + registry.get_or_emplace(childHierarchy->prev).next = childHierarchy->next; + } + + if (childHierarchy->next != entt::null) { + registry.get_or_emplace(childHierarchy->next).prev = childHierarchy->prev; + } + + childHierarchy->parent = entt::null; + childHierarchy->prev = entt::null; + childHierarchy->next = entt::null; +} + +void DetachChildren(entt::registry& registry, entt::entity parent) { + auto* parentHierarchy = registry.try_get(parent); + if (!parentHierarchy) { + return; + } + + entt::entity child = parentHierarchy->first; + parentHierarchy->first = entt::null; + + while (child != entt::null) { + auto* childHierarchy = registry.try_get(child); + entt::entity nextChild = childHierarchy ? childHierarchy->next : entt::null; + + if (childHierarchy) { + childHierarchy->parent = entt::null; + childHierarchy->prev = entt::null; + childHierarchy->next = entt::null; + } + + child = nextChild; + } +} + +static void UpdateWorldRecursive(entt::registry& registry, entt::entity entity, const WorldTransform* parentWorld) { + const WorldTransform* currentWorld = parentWorld; + + if (auto* local = registry.try_get(entity)) { + auto& world = registry.get_or_emplace(entity); + world = ComputeWorldFromParent(*local, parentWorld); + currentWorld = &world; + } + + auto* hierarchy = registry.try_get(entity); + if (!hierarchy) { + return; + } + + entt::entity child = hierarchy->first; + while (child != entt::null) { + auto* childHierarchy = registry.try_get(child); + entt::entity nextChild = childHierarchy ? childHierarchy->next : entt::null; + UpdateWorldRecursive(registry, child, currentWorld); + child = nextChild; + } +} + +void UpdateWorldTransforms(entt::registry& registry) { + // Roots: entities with no hierarchy, or hierarchy parent == null + auto transformView = registry.view(entt::exclude); + for (auto entity : transformView) { + UpdateWorldRecursive(registry, entity, nullptr); + } + + auto hierarchyView = registry.view(); + for (auto entity : hierarchyView) { + const auto& hierarchy = hierarchyView.get(entity); + if (hierarchy.parent == entt::null) { + UpdateWorldRecursive(registry, entity, nullptr); + } + } +} diff --git a/src/SceneLoader.cpp b/src/SceneLoader.cpp new file mode 100644 index 0000000..d7e8221 --- /dev/null +++ b/src/SceneLoader.cpp @@ -0,0 +1,963 @@ +#include "SceneLoader.h" +#include "ECSComponents.h" +#include "ModelManager.h" +#include "ShaderManager.h" +#include "SceneGraph.h" +#include "TextureManager.h" +#include "AssetManager.h" +#include "PhysFSManager.h" +#include "log/log.h" +#include "raylib.h" +#include "entt.hpp" +#include +#include +#include +#include +#include +#include + +static entt::registry* g_sceneRegistry = nullptr; +static ModelManager* g_sceneModelManager = nullptr; +static ShaderManager* g_sceneShaderManager = nullptr; +static TextureManager* g_sceneTextureManager = nullptr; +static AssetManager* g_sceneAssetManager = nullptr; +static std::string g_sceneScriptPath; +static bool g_sceneScriptDirty = false; + +void SetSceneContext(entt::registry* registry, ModelManager* modelManager, ShaderManager* shaderManager, TextureManager* textureManager) { + g_sceneRegistry = registry; + g_sceneModelManager = modelManager; + g_sceneShaderManager = shaderManager; + g_sceneTextureManager = textureManager; +} + +void SetSceneAssetManager(AssetManager* assetManager) { + g_sceneAssetManager = assetManager; +} + +void ClearScene() { + if (g_sceneRegistry) { + g_sceneRegistry->clear(); + } +} + +void SetSceneScriptPath(const std::string& path) { + if (g_sceneScriptPath != path) { + g_sceneScriptPath = path; + g_sceneScriptDirty = true; + } +} + +const std::string& GetSceneScriptPath() { + return g_sceneScriptPath; +} + +bool ConsumeSceneScriptPath(std::string& outPath) { + if (!g_sceneScriptDirty) { + return false; + } + outPath = g_sceneScriptPath; + g_sceneScriptDirty = false; + return true; +} + +struct SceneEntityDef { + std::string id; + std::string parent; + std::string tag; + bool hasTransform = false; + Vector3 position{0.0f, 0.0f, 0.0f}; + Vector3 scale{1.0f, 1.0f, 1.0f}; + Vector3 rotation{0.0f, 0.0f, 0.0f}; + + bool hasModelRenderer = false; + std::string model; + std::string modelAsset; + unsigned int modelId = 0; + bool hasModelId = false; + Vector3 modelSize{1.0f, 1.0f, 1.0f}; + float radius = 0.5f; + int rings = 16; + int slices = 16; + unsigned int color = 0xFFFFFFFF; + float outlineSize = 0.0f; + std::string shaderVs; + std::string shaderFs; + std::string shaderAsset; + unsigned int shaderId = 0; + bool hasShaderId = false; + + bool hasMaterial = false; + std::string materialAsset; + unsigned int materialId = 0; + bool hasMaterialId = false; + + bool hasScript = false; + std::string scriptPath; + bool scriptEnabled = true; + std::string scriptClass; + std::unordered_map scriptOverrides; + + bool hasTexture = false; + std::string texturePath; + std::string textureAsset; + unsigned int textureId = 0; + bool hasTextureId = false; + bool hasUV = false; + Vector2 uvScale{1.0f, 1.0f}; + Vector2 uvOffset{0.0f, 0.0f}; + float uvRotation = 0.0f; +}; + +static std::string Trim(const std::string& value) { + size_t start = 0; + while (start < value.size() && std::isspace(static_cast(value[start]))) { + start++; + } + size_t end = value.size(); + while (end > start && std::isspace(static_cast(value[end - 1]))) { + end--; + } + return value.substr(start, end - start); +} + +static std::string StripComment(const std::string& line) { + bool inString = false; + for (size_t i = 0; i < line.size(); ++i) { + char c = line[i]; + if (c == '"') { + inString = !inString; + } + if (!inString && c == '#') { + return line.substr(0, i); + } + } + return line; +} + +static bool ParseVec3(const std::string& value, Vector3& out) { + std::string trimmed = Trim(value); + if (trimmed.size() < 5 || trimmed.front() != '[' || trimmed.back() != ']') { + return false; + } + + std::string inner = trimmed.substr(1, trimmed.size() - 2); + std::stringstream ss(inner); + std::string token; + float vals[3] = {0.0f, 0.0f, 0.0f}; + int count = 0; + + while (std::getline(ss, token, ',')) { + if (count >= 3) break; + vals[count] = std::stof(Trim(token)); + count++; + } + + if (count != 3) return false; + out = {vals[0], vals[1], vals[2]}; + return true; +} + +static bool ParseVec2(const std::string& value, Vector2& out) { + std::string trimmed = Trim(value); + if (trimmed.size() < 5 || trimmed.front() != '[' || trimmed.back() != ']') { + return false; + } + + std::string inner = trimmed.substr(1, trimmed.size() - 2); + std::stringstream ss(inner); + std::string token; + float vals[2] = {0.0f, 0.0f}; + int count = 0; + + while (std::getline(ss, token, ',')) { + if (count >= 2) break; + vals[count] = std::stof(Trim(token)); + count++; + } + + if (count != 2) return false; + out = {vals[0], vals[1]}; + return true; +} + +static bool ParseRotationVector(const std::string& value, Vector3& out, float scale) { + Vector3 vec{}; + if (ParseVec3(value, vec)) { + out = {vec.x * scale, vec.y * scale, vec.z * scale}; + return true; + } + + std::string trimmed = Trim(value); + try { + float rot = std::stof(trimmed); + out = {0.0f, rot * scale, 0.0f}; + return true; + } catch (...) { + return false; + } +} + +static bool ParseHexColor(const std::string& value, unsigned int& out) { + std::string trimmed = Trim(value); + if (trimmed.rfind("0x", 0) == 0 || trimmed.rfind("0X", 0) == 0) { + out = static_cast(std::stoul(trimmed, nullptr, 16)); + return true; + } + if (!trimmed.empty() && trimmed.front() == '"' && trimmed.back() == '"') { + std::string inner = trimmed.substr(1, trimmed.size() - 2); + if (!inner.empty() && inner.front() == '#') { + inner = inner.substr(1); + } + out = static_cast(std::stoul(inner, nullptr, 16)); + return true; + } + return false; +} + +static bool ParseString(const std::string& value, std::string& out) { + std::string trimmed = Trim(value); + if (trimmed.size() >= 2 && trimmed.front() == '"' && trimmed.back() == '"') { + out = trimmed.substr(1, trimmed.size() - 2); + return true; + } + out = trimmed; + return !out.empty(); +} + +static bool ParseScriptOverrideValue(const std::string& value, std::string& out) { + // Preserve raw text for later type-aware parsing on apply. + std::string trimmed = Trim(value); + if (trimmed.empty()) return false; + if (trimmed.size() >= 2 && trimmed.front() == '"' && trimmed.back() == '"') { + out = trimmed.substr(1, trimmed.size() - 2); + return true; + } + out = trimmed; + return true; +} + +static bool ParseBool(const std::string& value, bool& out) { + std::string trimmed = Trim(value); + std::string lower; + lower.reserve(trimmed.size()); + for (char c : trimmed) { + lower.push_back(static_cast(std::tolower(static_cast(c)))); + } + if (lower == "true" || lower == "1") { + out = true; + return true; + } + if (lower == "false" || lower == "0") { + out = false; + return true; + } + return false; +} + +static std::string EscapeString(const std::string& value) { + std::string out; + out.reserve(value.size()); + for (char c : value) { + if (c == '"' || c == '\\') { + out.push_back('\\'); + } + out.push_back(c); + } + return out; +} + +static std::string FloatToString(float value) { + std::ostringstream ss; + ss << std::fixed << std::setprecision(3) << value; + return ss.str(); +} + +static std::string Vec3ToString(const Vector3& value) { + std::ostringstream ss; + ss << '[' + << FloatToString(value.x) << ", " + << FloatToString(value.y) << ", " + << FloatToString(value.z) << ']'; + return ss.str(); +} + +static std::string Vec2ToString(const Vector2& value) { + std::ostringstream ss; + ss << '[' + << FloatToString(value.x) << ", " + << FloatToString(value.y) << ']'; + return ss.str(); +} + +static std::string ColorToHexString(Color color) { + unsigned int packed = (static_cast(color.r) << 24) + | (static_cast(color.g) << 16) + | (static_cast(color.b) << 8) + | static_cast(color.a); + std::ostringstream ss; + ss << "0x" << std::uppercase << std::hex << std::setw(8) << std::setfill('0') << packed; + return ss.str(); +} + +static unsigned int LoadModelFor(const SceneEntityDef& def, std::unordered_map& modelCache) { + if (g_sceneAssetManager && !def.modelAsset.empty()) { + unsigned int existing = g_sceneAssetManager->GetModelId(def.modelAsset); + if (existing != 0) { + return existing; + } + + if (!def.model.empty()) { + if (def.model == "cube") { + return g_sceneAssetManager->LoadCube(def.modelAsset, def.modelSize.x, def.modelSize.y, def.modelSize.z); + } + if (def.model == "sphere") { + return g_sceneAssetManager->LoadSphere(def.modelAsset, def.radius, def.rings, def.slices); + } + if (def.model == "plane") { + return g_sceneAssetManager->LoadPlane(def.modelAsset, def.modelSize.x, def.modelSize.z, def.rings, def.slices); + } + return g_sceneAssetManager->LoadModel(def.modelAsset, def.model); + } + + if (def.hasModelId) { + g_sceneAssetManager->RegisterModel(def.modelAsset, def.modelId, "id"); + return def.modelId; + } + } + + if (!g_sceneModelManager || def.model.empty()) return 0; + + std::string key = def.model; + if (def.model == "cube") { + key += ":" + std::to_string(def.modelSize.x) + "," + std::to_string(def.modelSize.y) + "," + std::to_string(def.modelSize.z); + } else if (def.model == "sphere") { + key += ":" + std::to_string(def.radius) + "," + std::to_string(def.rings) + "," + std::to_string(def.slices); + } + + auto cached = modelCache.find(key); + if (cached != modelCache.end()) { + return cached->second; + } + + unsigned int modelId = 0; + if (def.model == "cube") { + modelId = g_sceneModelManager->LoadCube(def.modelSize.x, def.modelSize.y, def.modelSize.z); + } else if (def.model == "sphere") { + modelId = g_sceneModelManager->LoadSphere(def.radius, def.rings, def.slices); + } else if (def.model == "plane") { + modelId = g_sceneModelManager->LoadPlane(def.modelSize.x, def.modelSize.z, def.rings, def.slices); + } else { + modelId = g_sceneModelManager->Load(def.model); + } + + if (modelId != 0) { + modelCache[key] = modelId; + } + + return modelId; +} + +static unsigned int LoadShaderFor(const SceneEntityDef& def, std::unordered_map& shaderCache) { + if (g_sceneAssetManager && !def.shaderAsset.empty()) { + unsigned int existing = g_sceneAssetManager->GetShaderId(def.shaderAsset); + if (existing != 0) { + return existing; + } + + if (!def.shaderVs.empty() && !def.shaderFs.empty()) { + return g_sceneAssetManager->LoadShader(def.shaderAsset, def.shaderVs, def.shaderFs); + } + + if (def.hasShaderId) { + g_sceneAssetManager->RegisterShader(def.shaderAsset, def.shaderId, "id"); + return def.shaderId; + } + } + + if (!g_sceneShaderManager || def.shaderVs.empty() || def.shaderFs.empty()) return 0; + + std::string key = def.shaderVs + "|" + def.shaderFs; + auto cached = shaderCache.find(key); + if (cached != shaderCache.end()) { + return cached->second; + } + + unsigned int shaderId = g_sceneShaderManager->Load(def.shaderVs, def.shaderFs); + if (shaderId != 0) { + shaderCache[key] = shaderId; + } + return shaderId; +} + +static unsigned int LoadTextureFor(const SceneEntityDef& def, std::unordered_map& textureCache) { + if (g_sceneAssetManager && !def.textureAsset.empty()) { + unsigned int existing = g_sceneAssetManager->GetTextureId(def.textureAsset); + if (existing != 0) { + return existing; + } + + if (!def.texturePath.empty()) { + return g_sceneAssetManager->LoadTexture(def.textureAsset, def.texturePath); + } + + if (def.hasTextureId) { + g_sceneAssetManager->RegisterTexture(def.textureAsset, def.textureId, "id"); + return def.textureId; + } + } + + if (!g_sceneTextureManager || def.texturePath.empty()) return 0; + + auto cached = textureCache.find(def.texturePath); + if (cached != textureCache.end()) { + return cached->second; + } + + unsigned int textureId = g_sceneTextureManager->Load(def.texturePath); + if (textureId != 0) { + textureCache[def.texturePath] = textureId; + } + return textureId; +} + +entt::entity LoadSceneFromFile(const std::string& path, bool clearExisting) { + if (!g_sceneRegistry) { + log_error("Scene loader not initialized (registry missing)"); + return entt::null; + } + + if (clearExisting) { + g_sceneRegistry->clear(); + SetSceneScriptPath(std::string()); + } + + std::string contents; + if (!PhysFSManager::ReadTextFile(path, contents)) { + log_error("Failed to open scene file: %s", path.c_str()); + return entt::null; + } + + std::istringstream file(contents); + + std::vector defs; + SceneEntityDef* current = nullptr; + std::string line; + int lineNumber = 0; + + while (std::getline(file, line)) { + lineNumber++; + line = StripComment(line); + line = Trim(line); + if (line.empty()) continue; + + if (line == "[[entity]]") { + defs.emplace_back(); + current = &defs.back(); + continue; + } + + if (!current) { + size_t eq = line.find('='); + if (eq == std::string::npos) { + log_warn("Scene parse: invalid line %d", lineNumber); + continue; + } + + std::string key = Trim(line.substr(0, eq)); + std::string value = Trim(line.substr(eq + 1)); + + if (key == "scene_script") { + std::string sceneScript; + if (ParseString(value, sceneScript)) { + SetSceneScriptPath(sceneScript); + } + } else { + log_warn("Scene parse: key/value outside entity section at line %d", lineNumber); + } + continue; + } + + size_t eq = line.find('='); + if (eq == std::string::npos) { + log_warn("Scene parse: invalid line %d", lineNumber); + continue; + } + + std::string key = Trim(line.substr(0, eq)); + std::string value = Trim(line.substr(eq + 1)); + + if (key == "id") { + ParseString(value, current->id); + } else if (key == "parent") { + ParseString(value, current->parent); + } else if (key == "tag") { + ParseString(value, current->tag); + } else if (key == "position") { + current->hasTransform = ParseVec3(value, current->position); + } else if (key == "scale") { + current->hasTransform = ParseVec3(value, current->scale); + } else if (key == "rotation") { + if (ParseRotationVector(value, current->rotation, 1.0f)) { + current->hasTransform = true; + } + } else if (key == "rotation_deg") { + if (ParseRotationVector(value, current->rotation, DEG2RAD)) { + current->hasTransform = true; + } + } else if (key == "model") { + ParseString(value, current->model); + current->hasModelRenderer = true; + } else if (key == "model_asset") { + if (ParseString(value, current->modelAsset)) { + current->hasModelRenderer = true; + } + } else if (key == "model_id") { + current->modelId = static_cast(std::stoul(value, nullptr, 0)); + current->hasModelId = true; + current->hasModelRenderer = true; + } else if (key == "model_size") { + ParseVec3(value, current->modelSize); + } else if (key == "radius") { + current->radius = std::stof(value); + } else if (key == "rings") { + current->rings = std::stoi(value); + } else if (key == "slices") { + current->slices = std::stoi(value); + } else if (key == "color") { + ParseHexColor(value, current->color); + current->hasModelRenderer = true; + } else if (key == "outline_size") { + current->outlineSize = std::stof(value); + } else if (key == "shader_vs") { + ParseString(value, current->shaderVs); + } else if (key == "shader_fs") { + ParseString(value, current->shaderFs); + } else if (key == "shader_asset") { + if (ParseString(value, current->shaderAsset)) { + current->hasModelRenderer = true; + } + } else if (key == "shader_id") { + current->shaderId = static_cast(std::stoul(value, nullptr, 0)); + current->hasShaderId = true; + current->hasModelRenderer = true; + } else if (key == "material_asset") { + if (ParseString(value, current->materialAsset)) { + current->hasMaterial = true; + } + } else if (key == "material_id") { + current->materialId = static_cast(std::stoul(value, nullptr, 0)); + current->hasMaterialId = true; + current->hasMaterial = true; + } else if (key == "texture") { + if (ParseString(value, current->texturePath)) { + current->hasTexture = true; + } + } else if (key == "texture_asset") { + if (ParseString(value, current->textureAsset)) { + current->hasTexture = true; + } + } else if (key == "texture_id") { + current->textureId = static_cast(std::stoul(value, nullptr, 0)); + current->hasTextureId = true; + current->hasTexture = true; + } else if (key == "uv_scale") { + if (ParseVec2(value, current->uvScale)) { + current->hasUV = true; + } + } else if (key == "uv_offset") { + if (ParseVec2(value, current->uvOffset)) { + current->hasUV = true; + } + } else if (key == "uv_rotation") { + current->uvRotation = std::stof(value); + current->hasUV = true; + } else if (key == "script") { + if (ParseString(value, current->scriptPath)) { + current->hasScript = true; + } + } else if (key == "script_enabled") { + if (ParseBool(value, current->scriptEnabled)) { + current->hasScript = true; + } + } else if (key == "script_class") { + if (ParseString(value, current->scriptClass)) { + current->hasScript = true; + } + } else if (key.rfind("script_var_", 0) == 0) { + std::string fieldName = key.substr(std::string("script_var_").size()); + std::string rawValue; + if (ParseScriptOverrideValue(value, rawValue)) { + current->scriptOverrides[fieldName] = rawValue; + current->hasScript = true; + } + } else { + log_warn("Scene parse: unknown key '%s' at line %d", key.c_str(), lineNumber); + } + } + + if (defs.empty()) { + log_warn("Scene file contained no entities: %s", path.c_str()); + return entt::null; + } + + entt::entity root = g_sceneRegistry->create(); + g_sceneRegistry->emplace(root, "SceneRoot"); + g_sceneRegistry->emplace(root, Vector3{0.0f, 0.0f, 0.0f}, Vector3{1.0f, 1.0f, 1.0f}, 0.0f); + + std::unordered_map idMap; + std::vector created; + created.reserve(defs.size()); + std::unordered_map modelCache; + std::unordered_map shaderCache; + std::unordered_map textureCache; + + for (const auto& def : defs) { + entt::entity entity = g_sceneRegistry->create(); + created.push_back(entity); + + auto& sceneEntity = g_sceneRegistry->emplace(entity); + sceneEntity.persistent = true; + if (!def.id.empty()) { + sceneEntity.id = def.id; + } else { + sceneEntity.id = "entity_" + std::to_string(static_cast(entt::to_integral(entity))); + } + + if (!def.id.empty()) { + idMap[def.id] = entity; + } + + if (!def.tag.empty()) { + g_sceneRegistry->emplace(entity, def.tag); + } else if (!def.id.empty()) { + g_sceneRegistry->emplace(entity, def.id); + } + + if (def.hasTransform || def.hasModelRenderer) { + g_sceneRegistry->emplace(entity, def.position, def.scale, def.rotation); + } + + if (def.hasModelRenderer) { + unsigned int modelId = 0; + if (g_sceneAssetManager && !def.modelAsset.empty()) { + modelId = g_sceneAssetManager->GetModelId(def.modelAsset); + if (modelId == 0 && def.hasModelId) { + g_sceneAssetManager->RegisterModel(def.modelAsset, def.modelId, "id"); + modelId = def.modelId; + } + } + if (modelId == 0 && !def.model.empty()) { + modelId = LoadModelFor(def, modelCache); + } + if (modelId == 0 && def.hasModelId) { + modelId = def.modelId; + } + Color col = { + static_cast((def.color >> 24) & 0xFF), + static_cast((def.color >> 16) & 0xFF), + static_cast((def.color >> 8) & 0xFF), + static_cast(def.color & 0xFF) + }; + unsigned int shaderId = 0; + if (g_sceneAssetManager && !def.shaderAsset.empty()) { + shaderId = g_sceneAssetManager->GetShaderId(def.shaderAsset); + if (shaderId == 0 && def.hasShaderId) { + g_sceneAssetManager->RegisterShader(def.shaderAsset, def.shaderId, "id"); + shaderId = def.shaderId; + } + } + if (shaderId == 0 && !def.shaderVs.empty() && !def.shaderFs.empty()) { + shaderId = LoadShaderFor(def, shaderCache); + } + if (shaderId == 0 && def.hasShaderId) { + shaderId = def.shaderId; + } + g_sceneRegistry->emplace(entity, static_cast(modelId), col, def.outlineSize, shaderId); + } + + if (def.hasMaterial) { + unsigned int materialId = 0; + if (g_sceneAssetManager && !def.materialAsset.empty()) { + materialId = g_sceneAssetManager->GetMaterialId(def.materialAsset); + if (materialId == 0 && def.hasMaterialId) { + g_sceneAssetManager->RegisterMaterial(def.materialAsset, def.materialId, "id"); + materialId = def.materialId; + } + } + if (materialId == 0 && def.hasMaterialId) { + materialId = def.materialId; + } + g_sceneRegistry->emplace(entity, materialId); + } + + if (def.hasTexture) { + unsigned int textureId = 0; + if (g_sceneAssetManager && !def.textureAsset.empty()) { + textureId = g_sceneAssetManager->GetTextureId(def.textureAsset); + if (textureId == 0 && def.hasTextureId) { + g_sceneAssetManager->RegisterTexture(def.textureAsset, def.textureId, "id"); + textureId = def.textureId; + } + } + if (textureId == 0 && !def.texturePath.empty()) { + textureId = LoadTextureFor(def, textureCache); + } + if (textureId == 0 && def.hasTextureId) { + textureId = def.textureId; + } + g_sceneRegistry->emplace(entity, textureId); + } + + if (def.hasUV) { + g_sceneRegistry->emplace(entity, def.uvScale, def.uvOffset, def.uvRotation); + } + + if (def.hasScript || !def.scriptPath.empty()) { + auto& script = g_sceneRegistry->emplace(entity); + script.scriptPath = def.scriptPath; + script.enabled = def.scriptEnabled; + script.started = false; + script.className = def.scriptClass; + script.scriptOverrides = def.scriptOverrides; + } + } + + for (size_t i = 0; i < defs.size(); ++i) { + entt::entity entity = created[i]; + const auto& def = defs[i]; + + entt::entity parent = root; + if (!def.parent.empty()) { + auto it = idMap.find(def.parent); + if (it != idMap.end()) { + parent = it->second; + } else { + log_warn("Scene load: parent '%s' not found for entity '%s'", def.parent.c_str(), def.id.c_str()); + } + } + + AttachChild(*g_sceneRegistry, entity, parent); + } + + UpdateWorldTransforms(*g_sceneRegistry); + return root; +} + +bool SaveSceneToFile(const std::string& path) { + if (!g_sceneRegistry) { + log_error("Scene save failed (registry missing)"); + return false; + } + + auto parentPos = path.find_last_of("/\\"); + if (parentPos != std::string::npos) { + PhysFSManager::EnsureDir(path.substr(0, parentPos)); + } + + std::ostringstream file; + + if (!g_sceneScriptPath.empty()) { + file << "scene_script = \"" << EscapeString(g_sceneScriptPath) << "\"\n\n"; + } + + auto& registry = *g_sceneRegistry; + std::vector entities; + auto& storage = registry.storage(); + + for (auto entity : storage) { + if (!registry.valid(entity)) { + continue; + } + + auto* sceneEntity = registry.try_get(entity); + if (!sceneEntity || !sceneEntity->persistent) { + continue; + } + + entities.push_back(entity); + } + + std::sort(entities.begin(), entities.end(), [](entt::entity a, entt::entity b) { + return entt::to_integral(a) < entt::to_integral(b); + }); + + std::unordered_map idMap; + idMap.reserve(entities.size()); + for (auto entity : entities) { + auto* sceneEntity = registry.try_get(entity); + std::string idValue; + if (sceneEntity && !sceneEntity->id.empty()) { + idValue = sceneEntity->id; + } else { + std::ostringstream ss; + ss << "entity_" << static_cast(entt::to_integral(entity)); + idValue = ss.str(); + if (sceneEntity) { + sceneEntity->id = idValue; + } + } + idMap[entity] = idValue; + } + + for (auto entity : entities) { + file << "[[entity]]\n"; + file << "id = \"" << idMap[entity] << "\"\n"; + + if (auto* hierarchy = registry.try_get(entity)) { + if (hierarchy->parent != entt::null) { + auto it = idMap.find(hierarchy->parent); + if (it != idMap.end()) { + file << "parent = \"" << it->second << "\"\n"; + } + } + } + + if (auto* tag = registry.try_get(entity)) { + if (!tag->name.empty()) { + file << "tag = \"" << EscapeString(tag->name) << "\"\n"; + } + } + + if (auto* transform = registry.try_get(entity)) { + file << "position = " << Vec3ToString(transform->position) << "\n"; + file << "scale = " << Vec3ToString(transform->scale) << "\n"; + Vector3 rotDeg = { + transform->rotation.x * RAD2DEG, + transform->rotation.y * RAD2DEG, + transform->rotation.z * RAD2DEG + }; + file << "rotation_deg = " << Vec3ToString(rotDeg) << "\n"; + } + + if (auto* renderer = registry.try_get(entity)) { + bool wroteModel = false; + if (g_sceneAssetManager && renderer->modelId > 0) { + const std::string& modelKey = g_sceneAssetManager->GetModelKey(static_cast(renderer->modelId)); + if (!modelKey.empty()) { + file << "model_asset = \"" << EscapeString(modelKey) << "\"\n"; + } + } + if (g_sceneModelManager && renderer->modelId > 0) { + const ModelSource* source = g_sceneModelManager->GetSource(static_cast(renderer->modelId)); + if (source) { + switch (source->type) { + case ModelSourceType::Path: + if (!source->path.empty()) { + file << "model = \"" << EscapeString(source->path) << "\"\n"; + wroteModel = true; + } + break; + case ModelSourceType::Cube: + file << "model = \"cube\"\n"; + file << "model_size = " << Vec3ToString(source->size) << "\n"; + wroteModel = true; + break; + case ModelSourceType::Sphere: + file << "model = \"sphere\"\n"; + file << "radius = " << FloatToString(source->radius) << "\n"; + file << "rings = " << source->rings << "\n"; + file << "slices = " << source->slices << "\n"; + wroteModel = true; + break; + case ModelSourceType::Plane: + file << "model = \"plane\"\n"; + file << "model_size = " << Vec3ToString(source->size) << "\n"; + file << "rings = " << source->rings << "\n"; + file << "slices = " << source->slices << "\n"; + wroteModel = true; + break; + case ModelSourceType::None: + default: + break; + } + } + } + + if (!wroteModel && renderer->modelId > 0) { + file << "model_id = " << renderer->modelId << "\n"; + } + file << "color = " << ColorToHexString(renderer->color) << "\n"; + file << "outline_size = " << FloatToString(renderer->outlineSize) << "\n"; + bool wroteShader = false; + if (g_sceneAssetManager && renderer->shaderId > 0) { + const std::string& shaderKey = g_sceneAssetManager->GetShaderKey(renderer->shaderId); + if (!shaderKey.empty()) { + file << "shader_asset = \"" << EscapeString(shaderKey) << "\"\n"; + } + } + if (g_sceneShaderManager && renderer->shaderId > 0) { + const ShaderSource* source = g_sceneShaderManager->GetSource(renderer->shaderId); + if (source && !source->vsPath.empty() && !source->fsPath.empty()) { + file << "shader_vs = \"" << EscapeString(source->vsPath) << "\"\n"; + file << "shader_fs = \"" << EscapeString(source->fsPath) << "\"\n"; + wroteShader = true; + } + } + if (!wroteShader && renderer->shaderId > 0) { + file << "shader_id = " << renderer->shaderId << "\n"; + } + } + + if (auto* texture = registry.try_get(entity)) { + bool wroteTexture = false; + if (g_sceneAssetManager && texture->textureId > 0) { + const std::string& textureKey = g_sceneAssetManager->GetTextureKey(texture->textureId); + if (!textureKey.empty()) { + file << "texture_asset = \"" << EscapeString(textureKey) << "\"\n"; + } + } + if (g_sceneTextureManager && texture->textureId > 0) { + const TextureSource* source = g_sceneTextureManager->GetSource(texture->textureId); + if (source && !source->path.empty()) { + file << "texture = \"" << EscapeString(source->path) << "\"\n"; + wroteTexture = true; + } + } + if (!wroteTexture && texture->textureId > 0) { + file << "texture_id = " << texture->textureId << "\n"; + } + } + + if (auto* material = registry.try_get(entity)) { + bool wroteMaterial = false; + if (g_sceneAssetManager && material->materialId > 0) { + const std::string& materialKey = g_sceneAssetManager->GetMaterialKey(material->materialId); + if (!materialKey.empty()) { + file << "material_asset = \"" << EscapeString(materialKey) << "\"\n"; + wroteMaterial = true; + } + } + if (!wroteMaterial && material->materialId > 0) { + file << "material_id = " << material->materialId << "\n"; + } + } + + if (auto* uv = registry.try_get(entity)) { + file << "uv_scale = " << Vec2ToString(uv->scale) << "\n"; + file << "uv_offset = " << Vec2ToString(uv->offset) << "\n"; + file << "uv_rotation = " << FloatToString(uv->rotation) << "\n"; + } + + if (auto* script = registry.try_get(entity)) { + if (!script->scriptPath.empty()) { + file << "script = \"" << EscapeString(script->scriptPath) << "\"\n"; + } + if (!script->enabled) { + file << "script_enabled = false\n"; + } + if (!script->className.empty()) { + file << "script_class = \"" << EscapeString(script->className) << "\"\n"; + } + for (const auto& entry : script->scriptOverrides) { + if (entry.first.empty()) continue; + file << "script_var_" << entry.first << " = "; + file << '"' << EscapeString(entry.second) << '"' << "\n"; + } + } + + file << "\n"; + } + + return PhysFSManager::WriteTextFile(path, file.str()); +} diff --git a/src/ShaderManager.cpp b/src/ShaderManager.cpp new file mode 100644 index 0000000..30a5c95 --- /dev/null +++ b/src/ShaderManager.cpp @@ -0,0 +1,82 @@ +#include "ShaderManager.h" +#include "log/log.h" + +ShaderManager::ShaderManager() : nextId(1), unloaded(false) +{ +} + +ShaderManager::~ShaderManager() +{ + // Only unload if not already done + if (!unloaded) { + for (auto& pair : shaders) { + UnloadShader(pair.second); + } + shaders.clear(); + shaderSources.clear(); + } +} + +unsigned int ShaderManager::Load(const std::string& vsPath, const std::string& fsPath) +{ + Shader shader = LoadShader(vsPath.c_str(), fsPath.c_str()); + + if (shader.id == 0) + { + log_warn("Failed to load shader: %s / %s", vsPath.c_str(), fsPath.c_str()); + return 0; + } + + unsigned int id = nextId++; + shaders[id] = shader; + shaderSources[id] = ShaderSource{vsPath, fsPath}; + log_info("Loaded shader id=%u from %s / %s (raylib id=%u)", id, vsPath.c_str(), fsPath.c_str(), shader.id); + + return id; +} + +void ShaderManager::Unload(unsigned int id) +{ + auto it = shaders.find(id); + if (it != shaders.end()) + { + UnloadShader(it->second); + shaders.erase(it); + shaderSources.erase(id); + log_info("Unloaded shader id=%u", id); + } +} + +Shader* ShaderManager::Get(unsigned int id) +{ + auto it = shaders.find(id); + if (it != shaders.end()) + { + return &it->second; + } + return nullptr; +} + +const ShaderSource* ShaderManager::GetSource(unsigned int id) const +{ + auto it = shaderSources.find(id); + if (it != shaderSources.end()) + { + return &it->second; + } + return nullptr; +} + +void ShaderManager::UnloadAll() +{ + if (!unloaded) { + for (auto& pair : shaders) + { + UnloadShader(pair.second); + } + shaders.clear(); + shaderSources.clear(); + unloaded = true; + log_info("Unloaded all shaders"); + } +} diff --git a/src/TextureManager.cpp b/src/TextureManager.cpp new file mode 100644 index 0000000..d2baa77 --- /dev/null +++ b/src/TextureManager.cpp @@ -0,0 +1,74 @@ +#include "TextureManager.h" +#include "log/log.h" + +TextureManager::TextureManager() : nextId(1), unloaded(false) +{ +} + +TextureManager::~TextureManager() +{ + if (!unloaded) { + for (auto& pair : textures) { + UnloadTexture(pair.second); + } + textures.clear(); + textureSources.clear(); + } +} + +unsigned int TextureManager::Load(const std::string& path) +{ + Texture2D texture = LoadTexture(path.c_str()); + if (texture.id == 0) { + log_warn("Failed to load texture: %s", path.c_str()); + return 0; + } + + unsigned int id = nextId++; + textures[id] = texture; + textureSources[id] = TextureSource{path}; + log_info("Loaded texture id=%u from %s", id, path.c_str()); + return id; +} + +void TextureManager::Unload(unsigned int id) +{ + auto it = textures.find(id); + if (it != textures.end()) { + UnloadTexture(it->second); + textures.erase(it); + textureSources.erase(id); + log_info("Unloaded texture id=%u", id); + } +} + +Texture2D* TextureManager::Get(unsigned int id) +{ + auto it = textures.find(id); + if (it != textures.end()) { + return &it->second; + } + return nullptr; +} + +const TextureSource* TextureManager::GetSource(unsigned int id) const +{ + auto it = textureSources.find(id); + if (it != textureSources.end()) { + return &it->second; + } + return nullptr; +} + +void TextureManager::UnloadAll() +{ + if (!unloaded) { + for (auto& pair : textures) { + UnloadTexture(pair.second); + } + textures.clear(); + textureSources.clear(); + unloaded = true; + log_info("Unloaded all textures"); + } +} diff --git a/src/gui/GuiManager.cpp b/src/gui/GuiManager.cpp new file mode 100644 index 0000000..897e4e2 --- /dev/null +++ b/src/gui/GuiManager.cpp @@ -0,0 +1,2225 @@ +#include "gui/GuiManager.h" +#include "raylib.h" +#include "raymath.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "log.h" +#include "Application.h" +#include "ECSComponents.h" +#include "SceneGraph.h" +#include "SceneLoader.h" +#include "PhysFSManager.h" +#define IMVIEWGUIZMO_IMPLEMENTATION +#include "ImViewGuizmoRaymath.h" + +GuiManager::GuiManager() {} + +GuiManager::~GuiManager() {} + +void GuiManager::Initialize(Application *application) +{ + app = application; + rlImGuiSetup(true); + ImGuiIO &io = ImGui::GetIO(); + io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; + + SetTheme(); + + if (sceneLoadPath[0] == '\0') { + std::snprintf(sceneLoadPath, sizeof(sceneLoadPath), "scenes/demo.toml"); + } + if (sceneSavePath[0] == '\0') { + std::snprintf(sceneSavePath, sizeof(sceneSavePath), "scenes/demo.toml"); + } + + logWindow.Initialize(); // Initialize the LogWindow + log_trace("GuiManager::Initialize - Started"); +} + +void GuiManager::Render(RenderTexture2D &renderTexture) +{ + rlImGuiBegin(); + ImViewGuizmo::BeginFrame(); + SetupDockspace(renderTexture); + + RenderErrorBanner(); + RenderNotifications(); + logWindow.Render(showLogWindow); // Render the LogWindow + RenderSceneWindow(); + RenderAssetWindow(); + RenderInspectorWindow(); + + + rlImGuiEnd(); + + ImGui::UpdatePlatformWindows(); + ImGui::RenderPlatformWindowsDefault(); +} + +void GuiManager::ApplyRenderTextureResize(RenderTexture2D& renderTexture) +{ + if (desiredRenderSize.x < 1.0f || desiredRenderSize.y < 1.0f) { + return; + } + + const int targetWidth = std::max(1, static_cast(desiredRenderSize.x)); + const int targetHeight = std::max(1, static_cast(desiredRenderSize.y)); + + if (renderTexture.texture.id == 0 || + renderTexture.texture.width != targetWidth || + renderTexture.texture.height != targetHeight) { + if (renderTexture.texture.id != 0) { + UnloadRenderTexture(renderTexture); + } + renderTexture = LoadRenderTexture(targetWidth, targetHeight); + } +} + +void GuiManager::Shutdown() +{ + rlImGuiShutdown(); +} + +void GuiManager::SetupDockspace(RenderTexture2D &renderTexture) +{ + static bool dockspaceOpen = true; + static bool opt_fullscreen = true; + static ImGuiDockNodeFlags dockspace_flags = ImGuiDockNodeFlags_None; + + ImGuiWindowFlags window_flags = ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDocking; + if (opt_fullscreen) + { + ImGuiViewport *viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->WorkPos); + ImGui::SetNextWindowSize(viewport->WorkSize); + ImGui::SetNextWindowViewport(viewport->ID); + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); + window_flags |= ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove; + window_flags |= ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoNavFocus; + } + + ImGui::Begin("DockSpaceHost", &dockspaceOpen, window_flags); + if (opt_fullscreen) + ImGui::PopStyleVar(2); + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Game")) + { + if (ImGui::MenuItem("Quit", "Ctrl + Q")) + { + // Handle quit here + app->RequestShutdown(); + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Scene")) + { + if (ImGui::MenuItem("New", "Ctrl + N")) + { + requestSceneNew = true; + } + if (ImGui::MenuItem("Load...", "Ctrl + O")) + { + requestSceneLoad = true; + } + if (ImGui::MenuItem("Save As...", "Ctrl + S")) + { + requestSceneSave = true; + } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("View")) + { + ImGui::MenuItem("Log Viewer", "Ctrl + L", &showLogWindow); + ImGui::MenuItem("Scene", nullptr, &showSceneWindow); + ImGui::MenuItem("Assets", nullptr, &showAssetWindow); + bool useEditorCamera = app->IsUsingEditorCamera(); + if (ImGui::MenuItem("Use Editor Camera", nullptr, &useEditorCamera)) { + app->SetUseEditorCamera(useEditorCamera); + } + ImGui::MenuItem("Inspector", nullptr, &showInspectorWindow); + ImGui::EndMenu(); + } + ImGui::EndMenuBar(); + } + + if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_L)) + { + showLogWindow = !showLogWindow; + } + if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_Q)) + { + app->Shutdown(); + } + if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_N)) + { + requestSceneNew = true; + } + if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_O)) + { + requestSceneLoad = true; + } + if (ImGui::GetIO().KeyCtrl && ImGui::IsKeyPressed(ImGuiKey_S)) + { + requestSceneSave = true; + } + + if (requestSceneNew) + { + ImGui::OpenPopup("New Scene"); + requestSceneNew = false; + } + if (requestSceneLoad) + { + ImGui::OpenPopup("Load Scene"); + requestSceneLoad = false; + } + if (requestSceneSave) + { + ImGui::OpenPopup("Save Scene"); + requestSceneSave = false; + } + + if (ImGui::BeginPopupModal("New Scene", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::Text("Clear the current scene and start new?"); + ImGui::Spacing(); + + if (ImGui::Button("Create New")) + { + ClearScene(); + selectedEntity = entt::null; + tagBufferEntity = entt::null; + tagBuffer[0] = '\0'; + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + if (ImGui::BeginPopupModal("Load Scene", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::InputText("Path", sceneLoadPath, sizeof(sceneLoadPath)); + bool hasPath = sceneLoadPath[0] != '\0'; + if (!hasPath) + { + ImGui::BeginDisabled(); + } + if (ImGui::Button("Load")) + { + entt::entity root = LoadSceneFromFile(sceneLoadPath, true); + if (root != entt::null) + { + selectedEntity = entt::null; + tagBufferEntity = entt::null; + tagBuffer[0] = '\0'; + ImGui::CloseCurrentPopup(); + } + } + if (!hasPath) + { + ImGui::EndDisabled(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + + if (ImGui::BeginPopupModal("Save Scene", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) + { + ImGui::InputText("Path", sceneSavePath, sizeof(sceneSavePath)); + bool hasPath = sceneSavePath[0] != '\0'; + if (!hasPath) + { + ImGui::BeginDisabled(); + } + if (ImGui::Button("Save")) + { + if (SaveSceneToFile(sceneSavePath)) + { + ImGui::CloseCurrentPopup(); + } + } + if (!hasPath) + { + ImGui::EndDisabled(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) + { + ImGui::CloseCurrentPopup(); + } + ImGui::EndPopup(); + } + ImGuiID dockspace_id = ImGui::GetID("MyDockSpace"); + mainDockspaceId = dockspace_id; + ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags); + + // Add a Game Window + ImGui::Begin("Game Window"); + + // Render Raylib content to the Game Window + ImVec2 avail = ImGui::GetContentRegionAvail(); + ImVec2 windowPos = ImGui::GetCursorScreenPos(); + const int drawWidth = std::max(1, static_cast(avail.x)); + const int drawHeight = std::max(1, static_cast(avail.y)); + desiredRenderSize = ImVec2(static_cast(drawWidth), static_cast(drawHeight)); + + if (renderTexture.texture.id != 0 && drawWidth > 0 && drawHeight > 0) { + rlImGuiImageRect(&renderTexture.texture, drawWidth, drawHeight, + Rectangle{0, 0, static_cast(renderTexture.texture.width), -static_cast(renderTexture.texture.height)}); + } else { + ImGui::TextDisabled("No render target available"); + } + + ImGui::SetCursorScreenPos({windowPos.x + 12.0f, windowPos.y + 12.0f}); + bool useEditorCamera = app->IsUsingEditorCamera(); + const char* cameraLabel = useEditorCamera ? "Use Game Camera" : "Use Editor Camera"; + if (ImGui::Button(cameraLabel)) { + app->SetUseEditorCamera(!useEditorCamera); + } + + if (app->IsUsingEditorCamera()) { + Camera3D& editorCamera = app->GetEditorCamera(); + Vector3 camPos = editorCamera.position; + Vector3 pivot = editorCameraPivot; + const bool viewportHovered = ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem); + const bool flyActive = viewportHovered && IsMouseButtonDown(MOUSE_BUTTON_RIGHT); + + if (flyActive) { + editorPivotEntity = entt::null; + editorPivotOffset = {0.0f, 0.0f, 0.0f}; + editorCameraPivot = editorCamera.target; + editorPivotInitialized = true; + pivot = editorCamera.target; + } else if (selectedEntity != entt::null) { + auto& registry = app->GetRegistry(); + Vector3 entityPos = {0.0f, 0.0f, 0.0f}; + if (auto* worldTransform = registry.try_get(selectedEntity)) { + entityPos = worldTransform->position; + } else if (auto* localTransform = registry.try_get(selectedEntity)) { + entityPos = localTransform->position; + } + + if (editorPivotEntity != selectedEntity) { + editorPivotEntity = selectedEntity; + editorPivotOffset = {0.0f, 0.0f, 0.0f}; + } + + pivot = Vector3Add(entityPos, editorPivotOffset); + editorCameraPivot = pivot; + editorPivotInitialized = true; + } else { + editorPivotEntity = entt::null; + if (!editorPivotInitialized) { + editorCameraPivot = editorCamera.target; + editorPivotInitialized = true; + } + pivot = editorCameraPivot; + } + + Vector3 direction = Vector3Subtract(editorCamera.target, editorCamera.position); + if (Vector3Length(direction) <= 0.0001f) { + direction = {0.0f, 0.0f, 1.0f}; + } + direction = Vector3Normalize(direction); + + Quaternion camRot = ImViewGuizmo::GizmoMath::quatLookAt(direction, editorCamera.up); + + if (flyActive) { + Vector2 mouseDelta = GetMouseDelta(); + float yaw = std::atan2(direction.x, direction.z); + float pitch = std::asin(direction.y); + const float lookSensitivity = 0.003f; + + yaw -= mouseDelta.x * lookSensitivity; + pitch -= mouseDelta.y * lookSensitivity; + + const float maxPitch = 1.55f; + if (pitch > maxPitch) pitch = maxPitch; + if (pitch < -maxPitch) pitch = -maxPitch; + + Vector3 forward = { + std::sin(yaw) * std::cos(pitch), + std::sin(pitch), + std::cos(yaw) * std::cos(pitch) + }; + + Vector3 right = Vector3Normalize(Vector3CrossProduct(forward, editorCamera.up)); + Vector3 up = Vector3Normalize(Vector3CrossProduct(right, forward)); + + InputManager& input = app->GetInputManager(); + Vector3 move = {0.0f, 0.0f, 0.0f}; + if (input.IsActionDown("move_up")) { + move = Vector3Add(move, forward); + } + if (input.IsActionDown("move_down")) { + move = Vector3Subtract(move, forward); + } + if (input.IsActionDown("move_left")) { + move = Vector3Subtract(move, right); + } + if (input.IsActionDown("move_right")) { + move = Vector3Add(move, right); + } + + const float baseSpeed = 6.0f; + float speed = baseSpeed; + if (IsKeyDown(KEY_LEFT_SHIFT) || IsKeyDown(KEY_RIGHT_SHIFT)) { + speed *= 3.0f; + } + + if (Vector3Length(move) > 0.0001f) { + move = Vector3Scale(Vector3Normalize(move), speed * GetFrameTime()); + editorCamera.position = Vector3Add(editorCamera.position, move); + } + + editorCamera.target = Vector3Add(editorCamera.position, forward); + editorCamera.up = up; + editorCameraPivot = editorCamera.target; + } + + const float baseGizmoSize = 256.0f; + const float marginX = 6.0f; + const float marginY = 75.0f; + ImViewGuizmo::Style& gizmoStyle = ImViewGuizmo::GetStyle(); + + const float maxScaleX = (avail.x - (marginX * 2.0f)) / baseGizmoSize; + const float maxScaleY = (avail.y - (marginY * 2.0f) - 12.0f) / + (baseGizmoSize + (gizmoStyle.toolButtonRadius * 2.0f)); + float gizmoScale = std::min(1.0f, std::min(maxScaleX, maxScaleY)); + if (gizmoScale < 0.0f) { + gizmoScale = 0.0f; + } + gizmoStyle.scale = gizmoScale; + + const float gizmoSize = baseGizmoSize * gizmoScale; + if (gizmoSize <= 0.0f) { + ImGui::End(); + ImGui::End(); + return; + } + + ImVec2 gizmoPos = {windowPos.x + avail.x - gizmoSize - marginX, windowPos.y + marginY}; + ImVec2 dollyPos = {gizmoPos.x, gizmoPos.y + gizmoSize + 12.0f}; + ImVec2 panPos = {gizmoPos.x + 60.0f, gizmoPos.y + gizmoSize + 12.0f}; + + if (!flyActive) { + bool cameraChanged = false; + cameraChanged |= ImViewGuizmo::Rotate(camPos, camRot, pivot, gizmoPos); + cameraChanged |= ImViewGuizmo::Dolly(camPos, camRot, dollyPos); + Vector3 panStartPos = camPos; + bool panChanged = ImViewGuizmo::Pan(camPos, camRot, panPos); + cameraChanged |= panChanged; + if (panChanged) { + Vector3 delta = Vector3Subtract(camPos, panStartPos); + pivot = Vector3Add(pivot, delta); + if (selectedEntity != entt::null) { + editorPivotOffset = Vector3Add(editorPivotOffset, delta); + } else { + editorCameraPivot = pivot; + } + } + + if (cameraChanged) { + editorCamera.position = camPos; + Vector3 up = Vector3RotateByQuaternion({0.0f, 1.0f, 0.0f}, camRot); + editorCamera.target = pivot; + editorCamera.up = up; + } + } + } + + ImGui::End(); + ImGui::End(); +} + +static entt::id_type ToId(entt::entity entity) { + return entt::to_integral(entity); +} + +bool GuiManager::IsEntityAncestor(entt::entity ancestor, entt::entity entity) const +{ + if (!app) return false; + auto& registry = app->GetRegistry(); + entt::entity current = entity; + while (auto* hierarchy = registry.try_get(current)) { + if (hierarchy->parent == ancestor) { + return true; + } + if (hierarchy->parent == entt::null) { + return false; + } + current = hierarchy->parent; + } + return false; +} + +const char* GuiManager::GetEntityLabel(entt::entity entity) +{ + if (!app) return ""; + auto& registry = app->GetRegistry(); + if (auto* tag = registry.try_get(entity)) { + if (!tag->name.empty()) { + return tag->name.c_str(); + } + } + + static char label[64]; + std::snprintf(label, sizeof(label), "Entity %u", static_cast(ToId(entity))); + return label; +} + +void GuiManager::SyncTagBuffer(entt::entity entity) +{ + if (!app) return; + if (entity == tagBufferEntity) return; + + tagBufferEntity = entity; + tagBuffer[0] = '\0'; + auto& registry = app->GetRegistry(); + if (auto* tag = registry.try_get(entity)) { + std::snprintf(tagBuffer, sizeof(tagBuffer), "%s", tag->name.c_str()); + } +} + +void GuiManager::SyncScriptPathBuffer(entt::entity entity) +{ + if (!app) return; + if (entity == scriptPathEntity) return; + + scriptPathEntity = entity; + scriptPathBuffer[0] = '\0'; + auto& registry = app->GetRegistry(); + if (auto* script = registry.try_get(entity)) { + std::snprintf(scriptPathBuffer, sizeof(scriptPathBuffer), "%s", script->scriptPath.c_str()); + } +} + +void GuiManager::RenderEntityNode(entt::entity entity) +{ + if (!app) return; + auto& registry = app->GetRegistry(); + if (!registry.valid(entity)) return; + + auto* hierarchy = registry.try_get(entity); + bool hasChildren = hierarchy && hierarchy->first != entt::null; + + ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_SpanFullWidth; + if (entity == selectedEntity) { + flags |= ImGuiTreeNodeFlags_Selected; + } + if (!hasChildren) { + flags |= ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen; + } + + bool opened = ImGui::TreeNodeEx(reinterpret_cast(static_cast(ToId(entity))), flags, "%s", GetEntityLabel(entity)); + if (ImGui::IsItemClicked()) { + selectedEntity = entity; + SyncTagBuffer(entity); + } + + if (ImGui::BeginDragDropSource()) { + entt::entity payload = entity; + ImGui::SetDragDropPayload("SCENE_ENTITY", &payload, sizeof(payload)); + ImGui::Text("%s", GetEntityLabel(entity)); + ImGui::EndDragDropSource(); + } + + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("SCENE_ENTITY")) { + entt::entity dragged = *static_cast(payload->Data); + if (registry.valid(dragged) && dragged != entity && !IsEntityAncestor(dragged, entity)) { + AttachChild(registry, dragged, entity); + } + } + ImGui::EndDragDropTarget(); + } + + if (opened && hasChildren) { + entt::entity child = hierarchy->first; + while (child != entt::null) { + if (!registry.valid(child)) { + break; + } + RenderEntityNode(child); + auto* childHierarchy = registry.try_get(child); + child = childHierarchy ? childHierarchy->next : entt::null; + } + ImGui::TreePop(); + } +} + +void GuiManager::RenderSceneWindow() +{ + if (!showSceneWindow) return; + ImGui::Begin("Scene", &showSceneWindow); + + if (!app) { + ImGui::Text("No application context."); + ImGui::End(); + return; + } + + auto& registry = app->GetRegistry(); + + const std::string& sceneScriptPath = GetSceneScriptPath(); + if (sceneScriptPath != lastSceneScriptPath) { + std::snprintf(sceneScriptPathBuffer, sizeof(sceneScriptPathBuffer), "%s", sceneScriptPath.c_str()); + lastSceneScriptPath = sceneScriptPath; + } + + ImGui::Text("Scene Script"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(240.0f); + ImGui::InputText("##SceneScriptPath", sceneScriptPathBuffer, sizeof(sceneScriptPathBuffer)); + ImGui::SameLine(); + if (ImGui::Button("Apply")) { + SetSceneScriptPath(sceneScriptPathBuffer); + lastSceneScriptPath = sceneScriptPathBuffer; + } + + ImGui::Separator(); + + if (ImGui::Button("Create Entity")) { + entt::entity entity = registry.create(); + std::string name = "Entity " + std::to_string(static_cast(ToId(entity))); + registry.emplace(entity, name); + registry.emplace(entity, Vector3{0.0f, 0.0f, 0.0f}, Vector3{1.0f, 1.0f, 1.0f}, 0.0f); + auto& sceneEntity = registry.emplace(entity); + sceneEntity.persistent = true; + sceneEntity.id = "entity_" + std::to_string(static_cast(ToId(entity))); + selectedEntity = entity; + SyncTagBuffer(entity); + } + + ImGui::SameLine(); + bool canDelete = registry.valid(selectedEntity); + if (!canDelete) { + ImGui::BeginDisabled(); + } + if (ImGui::Button("Delete Selected") && canDelete) { + DetachChildren(registry, selectedEntity); + DetachFromParent(registry, selectedEntity); + registry.destroy(selectedEntity); + selectedEntity = entt::null; + tagBufferEntity = entt::null; + tagBuffer[0] = '\0'; + } + if (!canDelete) { + ImGui::EndDisabled(); + } + + ImGui::Separator(); + + std::vector roots; + auto &storage = registry.storage(); + for (auto entity : storage) { + if (!registry.valid(entity)) { + continue; + } + auto* hierarchy = registry.try_get(entity); + if (!hierarchy || hierarchy->parent == entt::null || !registry.valid(hierarchy->parent)) { + roots.push_back(entity); + } + } + + std::sort(roots.begin(), roots.end(), [](entt::entity a, entt::entity b) { + return ToId(a) < ToId(b); + }); + + for (auto entity : roots) { + RenderEntityNode(entity); + } + + ImGui::Separator(); + ImGui::TextDisabled("Drop here to clear parent"); + if (ImGui::BeginDragDropTarget()) { + if (const ImGuiPayload* payload = ImGui::AcceptDragDropPayload("SCENE_ENTITY")) { + entt::entity dragged = *static_cast(payload->Data); + if (registry.valid(dragged)) { + DetachFromParent(registry, dragged); + } + } + ImGui::EndDragDropTarget(); + } + + ImGui::End(); +} + +void GuiManager::RenderAssetWindow() +{ + if (!showAssetWindow) return; + if (mainDockspaceId != 0) { + ImGui::SetNextWindowDockID(mainDockspaceId, ImGuiCond_FirstUseEver); + } + ImGui::Begin("Assets", &showAssetWindow); + + if (!app) { + ImGui::Text("No application context."); + ImGui::End(); + return; + } + + auto& assets = app->GetAssetManager(); + + auto openUnloadPopup = [&](AssetUnloadType type, const std::string& key) { + pendingAssetUnloadType = type; + pendingAssetUnloadKey = key; + ImGui::OpenPopup("Unload Asset"); + }; + + auto assetTypeLabel = [](AssetUnloadType type) { + switch (type) { + case AssetUnloadType::Texture: return "texture"; + case AssetUnloadType::Shader: return "shader"; + case AssetUnloadType::Model: return "model"; + case AssetUnloadType::Material: return "material"; + default: return "asset"; + } + }; + + if (ImGui::BeginTabBar("AssetsTabs")) { + if (ImGui::BeginTabItem("Textures")) { + ImGui::InputText("Key", textureKeyBuffer, sizeof(textureKeyBuffer)); + ImGui::InputText("Path", texturePathBuffer, sizeof(texturePathBuffer)); + + bool canLoad = textureKeyBuffer[0] != '\0' && texturePathBuffer[0] != '\0'; + if (!canLoad) ImGui::BeginDisabled(); + if (ImGui::Button("Load Texture")) { + assets.LoadTexture(textureKeyBuffer, texturePathBuffer); + } + if (!canLoad) ImGui::EndDisabled(); + + auto keys = assets.ListTextureKeys(); + std::sort(keys.begin(), keys.end()); + + if (ImGui::BeginTable("TextureList", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("Key"); + ImGui::TableSetupColumn("Source"); + ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 90.0f); + ImGui::TableHeadersRow(); + for (const auto& key : keys) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(key.c_str()); + ImGui::TableSetColumnIndex(1); + std::string source = assets.GetTextureSource(key); + ImGui::TextUnformatted(source.c_str()); + ImGui::TableSetColumnIndex(2); + ImGui::PushID(key.c_str()); + if (ImGui::SmallButton("Unload")) { + openUnloadPopup(AssetUnloadType::Texture, key); + } + ImGui::PopID(); + } + ImGui::EndTable(); + } + + if (keys.empty()) { + ImGui::TextDisabled("No textures loaded."); + } + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Shaders")) { + ImGui::InputText("Key", shaderKeyBuffer, sizeof(shaderKeyBuffer)); + ImGui::InputText("Vertex Path", shaderVsPathBuffer, sizeof(shaderVsPathBuffer)); + ImGui::InputText("Fragment Path", shaderFsPathBuffer, sizeof(shaderFsPathBuffer)); + + bool canLoad = shaderKeyBuffer[0] != '\0' && shaderVsPathBuffer[0] != '\0' && shaderFsPathBuffer[0] != '\0'; + if (!canLoad) ImGui::BeginDisabled(); + if (ImGui::Button("Load Shader")) { + assets.LoadShader(shaderKeyBuffer, shaderVsPathBuffer, shaderFsPathBuffer); + } + if (!canLoad) ImGui::EndDisabled(); + + auto keys = assets.ListShaderKeys(); + std::sort(keys.begin(), keys.end()); + + if (ImGui::BeginTable("ShaderList", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("Key"); + ImGui::TableSetupColumn("Source"); + ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 90.0f); + ImGui::TableHeadersRow(); + for (const auto& key : keys) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(key.c_str()); + ImGui::TableSetColumnIndex(1); + std::string source = assets.GetShaderSource(key); + ImGui::TextUnformatted(source.c_str()); + ImGui::TableSetColumnIndex(2); + ImGui::PushID(key.c_str()); + if (ImGui::SmallButton("Unload")) { + openUnloadPopup(AssetUnloadType::Shader, key); + } + ImGui::PopID(); + } + ImGui::EndTable(); + } + + if (keys.empty()) { + ImGui::TextDisabled("No shaders loaded."); + } + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Models")) { + ImGui::InputText("Key", modelKeyBuffer, sizeof(modelKeyBuffer)); + ImGui::InputText("Path", modelPathBuffer, sizeof(modelPathBuffer)); + + bool canLoad = modelKeyBuffer[0] != '\0' && modelPathBuffer[0] != '\0'; + if (!canLoad) ImGui::BeginDisabled(); + if (ImGui::Button("Load Model")) { + assets.LoadModel(modelKeyBuffer, modelPathBuffer); + } + if (!canLoad) ImGui::EndDisabled(); + + ImGui::Separator(); + ImGui::Text("Primitive"); + + const char* primitiveLabels[] = { + "Cube", "Sphere", "HemiSphere", "Plane", "Cylinder", "Cone", "Torus", "Knot", "Poly", "Heightmap", "Cubicmap" + }; + ImGui::Combo("Type", &modelPrimitiveIndex, primitiveLabels, IM_ARRAYSIZE(primitiveLabels)); + + switch (modelPrimitiveIndex) { + case 0: + ImGui::DragFloat("Width", &modelWidth, 0.05f, 0.01f, 100.0f); + ImGui::DragFloat("Height", &modelHeight, 0.05f, 0.01f, 100.0f); + ImGui::DragFloat("Length", &modelLength, 0.05f, 0.01f, 100.0f); + break; + case 1: + case 2: + ImGui::DragFloat("Radius", &modelRadius, 0.05f, 0.01f, 100.0f); + ImGui::InputInt("Rings", &modelRings); + ImGui::InputInt("Slices", &modelSlices); + break; + case 3: + ImGui::DragFloat("Width", &modelWidth, 0.05f, 0.01f, 100.0f); + ImGui::DragFloat("Length", &modelLength, 0.05f, 0.01f, 100.0f); + ImGui::InputInt("Res X", &modelResX); + ImGui::InputInt("Res Z", &modelResZ); + break; + case 4: + case 5: + ImGui::DragFloat("Radius", &modelRadius, 0.05f, 0.01f, 100.0f); + ImGui::DragFloat("Height", &modelHeight, 0.05f, 0.01f, 100.0f); + ImGui::InputInt("Slices", &modelSlices); + break; + case 6: + case 7: + ImGui::DragFloat("Radius", &modelRadius, 0.05f, 0.01f, 100.0f); + ImGui::DragFloat("Size", &modelLength, 0.05f, 0.01f, 100.0f); + ImGui::InputInt("Rad Seg", &modelRadSeg); + ImGui::InputInt("Sides", &modelSides); + break; + case 8: + ImGui::InputInt("Sides", &modelPolySides); + ImGui::DragFloat("Radius", &modelRadius, 0.05f, 0.01f, 100.0f); + break; + case 9: + ImGui::InputText("Heightmap Path", modelHeightmapPathBuffer, sizeof(modelHeightmapPathBuffer)); + ImGui::DragFloat("Size X", &modelSizeX, 0.05f, 0.01f, 1000.0f); + ImGui::DragFloat("Size Y", &modelSizeY, 0.05f, 0.01f, 1000.0f); + ImGui::DragFloat("Size Z", &modelSizeZ, 0.05f, 0.01f, 1000.0f); + break; + case 10: + ImGui::InputText("Cubicmap Path", modelCubicmapPathBuffer, sizeof(modelCubicmapPathBuffer)); + ImGui::DragFloat("Size X", &modelSizeX, 0.05f, 0.01f, 1000.0f); + ImGui::DragFloat("Size Y", &modelSizeY, 0.05f, 0.01f, 1000.0f); + ImGui::DragFloat("Size Z", &modelSizeZ, 0.05f, 0.01f, 1000.0f); + break; + default: + break; + } + + if (ImGui::Button("Create Primitive")) { + std::string key = modelKeyBuffer; + if (key.empty()) { + std::snprintf(modelKeyBuffer, sizeof(modelKeyBuffer), "%s_%d", + primitiveLabels[modelPrimitiveIndex], ++modelAutoKeyCounter); + key = modelKeyBuffer; + } + + switch (modelPrimitiveIndex) { + case 0: + assets.LoadCube(key, modelWidth, modelHeight, modelLength); + break; + case 1: + assets.LoadSphere(key, modelRadius, modelRings, modelSlices); + break; + case 2: + assets.LoadHemiSphere(key, modelRadius, modelRings, modelSlices); + break; + case 3: + assets.LoadPlane(key, modelWidth, modelLength, modelResX, modelResZ); + break; + case 4: + assets.LoadCylinder(key, modelRadius, modelHeight, modelSlices); + break; + case 5: + assets.LoadCone(key, modelRadius, modelHeight, modelSlices); + break; + case 6: + assets.LoadTorus(key, modelRadius, modelLength, modelRadSeg, modelSides); + break; + case 7: + assets.LoadKnot(key, modelRadius, modelLength, modelRadSeg, modelSides); + break; + case 8: + assets.LoadPoly(key, modelPolySides, modelRadius); + break; + case 9: + if (modelHeightmapPathBuffer[0] != '\0') { + assets.LoadHeightmap(key, modelHeightmapPathBuffer, modelSizeX, modelSizeY, modelSizeZ); + } + break; + case 10: + if (modelCubicmapPathBuffer[0] != '\0') { + assets.LoadCubicmap(key, modelCubicmapPathBuffer, modelSizeX, modelSizeY, modelSizeZ); + } + break; + default: + break; + } + } + ImGui::TextDisabled("Leave key empty to auto-generate."); + + auto keys = assets.ListModelKeys(); + std::sort(keys.begin(), keys.end()); + + if (ImGui::BeginTable("ModelList", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("Key"); + ImGui::TableSetupColumn("Source"); + ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 90.0f); + ImGui::TableHeadersRow(); + for (const auto& key : keys) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(key.c_str()); + ImGui::TableSetColumnIndex(1); + std::string source = assets.GetModelSource(key); + ImGui::TextUnformatted(source.c_str()); + ImGui::TableSetColumnIndex(2); + ImGui::PushID(key.c_str()); + if (ImGui::SmallButton("Unload")) { + openUnloadPopup(AssetUnloadType::Model, key); + } + ImGui::PopID(); + } + ImGui::EndTable(); + } + + if (keys.empty()) { + ImGui::TextDisabled("No models loaded."); + } + + ImGui::EndTabItem(); + } + + if (ImGui::BeginTabItem("Materials")) { + ImGui::InputText("Key", materialKeyBuffer, sizeof(materialKeyBuffer)); + + auto shaderKeys = assets.ListShaderKeys(); + std::sort(shaderKeys.begin(), shaderKeys.end()); + if (shaderKeys.empty()) { + ImGui::InputInt("Shader Id", &materialShaderId); + } else { + if (materialShaderIndex < 0 || materialShaderIndex >= static_cast(shaderKeys.size())) { + materialShaderIndex = 0; + } + std::vector keyLabels; + keyLabels.reserve(shaderKeys.size()); + for (const auto& key : shaderKeys) { + keyLabels.push_back(key.c_str()); + } + ImGui::Combo("Shader Asset", &materialShaderIndex, keyLabels.data(), static_cast(keyLabels.size())); + } + + ImGui::ColorEdit4("Albedo", materialAlbedo); + + bool canCreate = materialKeyBuffer[0] != '\0'; + if (!canCreate) ImGui::BeginDisabled(); + if (ImGui::Button("Create Material")) { + unsigned int shaderId = 0; + if (shaderKeys.empty()) { + shaderId = materialShaderId < 0 ? 0u : static_cast(materialShaderId); + } else { + shaderId = assets.GetShaderId(shaderKeys[materialShaderIndex]); + } + Color albedo = { + static_cast(materialAlbedo[0] * 255.0f), + static_cast(materialAlbedo[1] * 255.0f), + static_cast(materialAlbedo[2] * 255.0f), + static_cast(materialAlbedo[3] * 255.0f) + }; + assets.CreateMaterial(materialKeyBuffer, shaderId, albedo); + } + if (!canCreate) ImGui::EndDisabled(); + + auto keys = assets.ListMaterialKeys(); + std::sort(keys.begin(), keys.end()); + + if (ImGui::BeginTable("MaterialList", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) { + ImGui::TableSetupColumn("Key"); + ImGui::TableSetupColumn("Source"); + ImGui::TableSetupColumn("Actions", ImGuiTableColumnFlags_WidthFixed, 90.0f); + ImGui::TableHeadersRow(); + for (const auto& key : keys) { + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); + ImGui::TextUnformatted(key.c_str()); + ImGui::TableSetColumnIndex(1); + std::string source = assets.GetMaterialSource(key); + ImGui::TextUnformatted(source.c_str()); + ImGui::TableSetColumnIndex(2); + ImGui::PushID(key.c_str()); + if (ImGui::SmallButton("Unload")) { + openUnloadPopup(AssetUnloadType::Material, key); + } + ImGui::PopID(); + } + ImGui::EndTable(); + } + + if (keys.empty()) { + ImGui::TextDisabled("No materials loaded."); + } + + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); + } + + if (ImGui::BeginPopupModal("Unload Asset", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { + const char* typeLabel = assetTypeLabel(pendingAssetUnloadType); + ImGui::Text("Unload %s '%s'?", typeLabel, pendingAssetUnloadKey.c_str()); + ImGui::Spacing(); + + if (ImGui::Button("Unload")) { + switch (pendingAssetUnloadType) { + case AssetUnloadType::Texture: + assets.UnloadTexture(pendingAssetUnloadKey); + break; + case AssetUnloadType::Shader: + assets.UnloadShader(pendingAssetUnloadKey); + break; + case AssetUnloadType::Model: + assets.UnloadModel(pendingAssetUnloadKey); + break; + case AssetUnloadType::Material: + assets.UnloadMaterial(pendingAssetUnloadKey); + break; + default: + break; + } + pendingAssetUnloadType = AssetUnloadType::None; + pendingAssetUnloadKey.clear(); + ImGui::CloseCurrentPopup(); + } + ImGui::SameLine(); + if (ImGui::Button("Cancel")) { + pendingAssetUnloadType = AssetUnloadType::None; + pendingAssetUnloadKey.clear(); + ImGui::CloseCurrentPopup(); + } + + ImGui::EndPopup(); + } + + ImGui::End(); +} + +void GuiManager::RenderInspectorWindow() +{ + if (!showInspectorWindow) return; + ImGui::Begin("Inspector", &showInspectorWindow); + + if (!app) { + ImGui::Text("No application context."); + ImGui::End(); + return; + } + + auto& registry = app->GetRegistry(); + auto& assets = app->GetAssetManager(); + if (!registry.valid(selectedEntity)) { + ImGui::Text("No entity selected."); + ImGui::End(); + return; + } + + ImGui::Text("%s", GetEntityLabel(selectedEntity)); + ImGui::Separator(); + + struct ComponentOption { + const char* label; + int id; + }; + + const ComponentOption allOptions[] = { + {"Tag", 0}, + {"Transform", 1}, + {"Velocity", 2}, + {"Model Renderer", 3}, + {"Script", 4}, + {"Camera", 5}, + {"Light", 6}, + {"Material", 7}, + {"Render Pass", 8}, + {"Texture", 9}, + {"UV Transform", 10} + }; + + auto hasComponent = [&](int id) { + switch (id) { + case 0: return registry.any_of(selectedEntity); + case 1: return registry.any_of(selectedEntity); + case 2: return registry.any_of(selectedEntity); + case 3: return registry.any_of(selectedEntity); + case 4: return registry.any_of(selectedEntity); + case 5: return registry.any_of(selectedEntity); + case 6: return registry.any_of(selectedEntity); + case 7: return registry.any_of(selectedEntity); + case 8: return registry.any_of(selectedEntity); + case 9: return registry.any_of(selectedEntity); + case 10: return registry.any_of(selectedEntity); + default: return false; + } + }; + + std::vector availableLabels; + std::vector availableIds; + availableLabels.reserve(sizeof(allOptions) / sizeof(allOptions[0])); + availableIds.reserve(sizeof(allOptions) / sizeof(allOptions[0])); + + for (const auto& option : allOptions) { + if (!hasComponent(option.id)) { + availableLabels.push_back(option.label); + availableIds.push_back(option.id); + } + } + + ImGui::Text("Add Component"); + ImGui::SameLine(); + ImGui::SetNextItemWidth(180.0f); + + if (availableLabels.empty()) { + ImGui::TextDisabled("All added"); + } else { + if (addComponentIndex < 0 || addComponentIndex >= static_cast(availableLabels.size())) { + addComponentIndex = 0; + } + ImGui::Combo("##AddComponentCombo", &addComponentIndex, availableLabels.data(), static_cast(availableLabels.size())); + } + + bool canAddComponent = !availableIds.empty(); + + if (!canAddComponent) { + ImGui::BeginDisabled(); + } + if (ImGui::Button("Add")) { + int selectedId = canAddComponent ? availableIds[addComponentIndex] : -1; + switch (selectedId) { + case 0: { + registry.emplace(selectedEntity, "Entity"); + SyncTagBuffer(selectedEntity); + break; + } + case 1: + registry.emplace(selectedEntity, Vector3{0.0f, 0.0f, 0.0f}, Vector3{1.0f, 1.0f, 1.0f}, 0.0f); + break; + case 2: + registry.emplace(selectedEntity, Vector3{0.0f, 0.0f, 0.0f}, 0.0f); + break; + case 3: + registry.emplace(selectedEntity, 0, WHITE, 0.0f, 0); + break; + case 4: + registry.emplace(selectedEntity); + SyncScriptPathBuffer(selectedEntity); + break; + case 5: + registry.emplace(selectedEntity); + break; + case 6: + registry.emplace(selectedEntity); + break; + case 7: + registry.emplace(selectedEntity); + break; + case 8: + registry.emplace(selectedEntity); + break; + case 9: + registry.emplace(selectedEntity, 0); + break; + case 10: + registry.emplace(selectedEntity); + break; + default: + break; + } + } + if (!canAddComponent) { + ImGui::EndDisabled(); + } + ImGui::Separator(); + + auto normalizeVector = [](Vector3 value, Vector3 fallback = Vector3{0.0f, 1.0f, 0.0f}) { + float length = std::sqrt(value.x * value.x + value.y * value.y + value.z * value.z); + if (length <= 0.0001f) { + return fallback; + } + return Vector3{value.x / length, value.y / length, value.z / length}; + }; + + // Tag + if (registry.any_of(selectedEntity)) { + SyncTagBuffer(selectedEntity); + if (ImGui::InputText("Tag", tagBuffer, sizeof(tagBuffer))) { + auto& tag = registry.get(selectedEntity); + tag.name = tagBuffer; + } + } + + // Transform + if (registry.any_of(selectedEntity)) { + ImGui::PushID("Transform"); + auto& transform = registry.get(selectedEntity); + float position[3] = {transform.position.x, transform.position.y, transform.position.z}; + float scale[3] = {transform.scale.x, transform.scale.y, transform.scale.z}; + float rotationDeg[3] = { + transform.rotation.x * RAD2DEG, + transform.rotation.y * RAD2DEG, + transform.rotation.z * RAD2DEG + }; + + if (ImGui::DragFloat3("Position", position, 0.1f)) { + transform.position = {position[0], position[1], position[2]}; + } + if (ImGui::DragFloat3("Scale", scale, 0.05f)) { + transform.scale = {scale[0], scale[1], scale[2]}; + } + if (ImGui::DragFloat3("Rotation (deg)", rotationDeg, 0.5f)) { + transform.rotation = { + rotationDeg[0] * DEG2RAD, + rotationDeg[1] * DEG2RAD, + rotationDeg[2] * DEG2RAD + }; + } + ImGui::PopID(); + } + + // Velocity + if (registry.any_of(selectedEntity)) { + auto& velocity = registry.get(selectedEntity); + ImGui::Separator(); + ImGui::Text("Velocity"); + + float linear[3] = {velocity.linear.x, velocity.linear.y, velocity.linear.z}; + if (ImGui::DragFloat3("Linear", linear, 0.1f)) { + velocity.linear = {linear[0], linear[1], linear[2]}; + } + + float angular = velocity.angular; + if (ImGui::DragFloat("Angular (rad/s)", &angular, 0.05f)) { + velocity.angular = angular; + } + + if (ImGui::Button("Remove Velocity")) { + registry.remove(selectedEntity); + } + } + + // Hierarchy + if (auto* hierarchy = registry.try_get(selectedEntity)) { + ImGui::Separator(); + ImGui::Text("Hierarchy"); + if (hierarchy->parent != entt::null) { + ImGui::Text("Parent: %s", GetEntityLabel(hierarchy->parent)); + if (ImGui::Button("Clear Parent")) { + DetachFromParent(registry, selectedEntity); + } + } else { + ImGui::Text("Parent: "); + } + } + + // World Transform (computed) + if (auto* world = registry.try_get(selectedEntity)) { + ImGui::Separator(); + ImGui::Text("World Transform (computed)"); + ImGui::BeginDisabled(); + + float worldPos[3] = {world->position.x, world->position.y, world->position.z}; + float worldScale[3] = {world->scale.x, world->scale.y, world->scale.z}; + float worldRotDeg[3] = { + world->rotation.x * RAD2DEG, + world->rotation.y * RAD2DEG, + world->rotation.z * RAD2DEG + }; + + ImGui::DragFloat3("World Position", worldPos, 0.1f); + ImGui::DragFloat3("World Scale", worldScale, 0.05f); + ImGui::DragFloat3("World Rotation (deg)", worldRotDeg, 0.5f); + + ImGui::EndDisabled(); + } + + // Model Renderer + if (registry.any_of(selectedEntity)) { + auto& renderer = registry.get(selectedEntity); + ImGui::Separator(); + ImGui::Text("Model Renderer"); + + auto modelKeys = assets.ListModelKeys(); + std::sort(modelKeys.begin(), modelKeys.end()); + std::vector modelLabels; + modelLabels.reserve(modelKeys.size() + 1); + modelLabels.push_back(""); + for (const auto& key : modelKeys) { + modelLabels.push_back(key); + } + std::vector modelLabelPtrs; + modelLabelPtrs.reserve(modelLabels.size()); + for (const auto& label : modelLabels) { + modelLabelPtrs.push_back(label.c_str()); + } + + int modelIndex = 0; + std::string currentModelKey = assets.GetModelKey(static_cast(renderer.modelId)); + if (!currentModelKey.empty()) { + auto it = std::find(modelKeys.begin(), modelKeys.end(), currentModelKey); + if (it != modelKeys.end()) { + modelIndex = static_cast(std::distance(modelKeys.begin(), it)) + 1; + } + } + int selectedModelIndex = modelIndex; + if (ImGui::Combo("Model Asset", &selectedModelIndex, modelLabelPtrs.data(), static_cast(modelLabelPtrs.size()))) { + if (selectedModelIndex == 0) { + renderer.modelId = 0; + } else { + renderer.modelId = assets.GetModelId(modelKeys[selectedModelIndex - 1]); + } + } + + int modelId = static_cast(renderer.modelId); + if (ImGui::InputInt("Model Id", &modelId)) { + renderer.modelId = modelId < 0 ? 0 : static_cast(modelId); + } + + auto shaderKeys = assets.ListShaderKeys(); + std::sort(shaderKeys.begin(), shaderKeys.end()); + std::vector shaderLabels; + shaderLabels.reserve(shaderKeys.size() + 1); + shaderLabels.push_back(""); + for (const auto& key : shaderKeys) { + shaderLabels.push_back(key); + } + std::vector shaderLabelPtrs; + shaderLabelPtrs.reserve(shaderLabels.size()); + for (const auto& label : shaderLabels) { + shaderLabelPtrs.push_back(label.c_str()); + } + + int shaderIndex = 0; + std::string currentShaderKey = assets.GetShaderKey(renderer.shaderId); + if (!currentShaderKey.empty()) { + auto it = std::find(shaderKeys.begin(), shaderKeys.end(), currentShaderKey); + if (it != shaderKeys.end()) { + shaderIndex = static_cast(std::distance(shaderKeys.begin(), it)) + 1; + } + } + int selectedShaderIndex = shaderIndex; + if (ImGui::Combo("Shader Asset", &selectedShaderIndex, shaderLabelPtrs.data(), static_cast(shaderLabelPtrs.size()))) { + if (selectedShaderIndex == 0) { + renderer.shaderId = 0; + } else { + renderer.shaderId = assets.GetShaderId(shaderKeys[selectedShaderIndex - 1]); + } + } + + int shaderId = static_cast(renderer.shaderId); + if (ImGui::InputInt("Shader Id", &shaderId)) { + renderer.shaderId = shaderId < 0 ? 0 : static_cast(shaderId); + } + + float color[4] = { + renderer.color.r / 255.0f, + renderer.color.g / 255.0f, + renderer.color.b / 255.0f, + renderer.color.a / 255.0f + }; + if (ImGui::ColorEdit4("Color", color)) { + renderer.color.r = static_cast(color[0] * 255.0f); + renderer.color.g = static_cast(color[1] * 255.0f); + renderer.color.b = static_cast(color[2] * 255.0f); + renderer.color.a = static_cast(color[3] * 255.0f); + } + + ImGui::DragFloat("Outline", &renderer.outlineSize, 0.05f, 0.0f, 10.0f); + + if (ImGui::Button("Remove Model Renderer")) { + registry.remove(selectedEntity); + } + } + + // Texture + if (registry.any_of(selectedEntity)) { + auto& texture = registry.get(selectedEntity); + ImGui::Separator(); + ImGui::Text("Texture"); + + auto textureKeys = assets.ListTextureKeys(); + std::sort(textureKeys.begin(), textureKeys.end()); + std::vector textureLabels; + textureLabels.reserve(textureKeys.size() + 1); + textureLabels.push_back(""); + for (const auto& key : textureKeys) { + textureLabels.push_back(key); + } + std::vector textureLabelPtrs; + textureLabelPtrs.reserve(textureLabels.size()); + for (const auto& label : textureLabels) { + textureLabelPtrs.push_back(label.c_str()); + } + + int textureIndex = 0; + std::string currentTextureKey = assets.GetTextureKey(texture.textureId); + if (!currentTextureKey.empty()) { + auto it = std::find(textureKeys.begin(), textureKeys.end(), currentTextureKey); + if (it != textureKeys.end()) { + textureIndex = static_cast(std::distance(textureKeys.begin(), it)) + 1; + } + } + int selectedTextureIndex = textureIndex; + if (ImGui::Combo("Texture Asset", &selectedTextureIndex, textureLabelPtrs.data(), static_cast(textureLabelPtrs.size()))) { + if (selectedTextureIndex == 0) { + texture.textureId = 0; + } else { + texture.textureId = assets.GetTextureId(textureKeys[selectedTextureIndex - 1]); + } + } + + int textureId = static_cast(texture.textureId); + if (ImGui::InputInt("Texture Id", &textureId)) { + texture.textureId = textureId < 0 ? 0u : static_cast(textureId); + } + + if (ImGui::Button("Remove Texture")) { + registry.remove(selectedEntity); + } + } + + // UV Transform + if (registry.any_of(selectedEntity)) { + auto& uv = registry.get(selectedEntity); + ImGui::Separator(); + ImGui::Text("UV Transform"); + + float scale[2] = {uv.scale.x, uv.scale.y}; + if (ImGui::DragFloat2("Scale", scale, 0.01f)) { + uv.scale = {scale[0], scale[1]}; + } + + float offset[2] = {uv.offset.x, uv.offset.y}; + if (ImGui::DragFloat2("Offset", offset, 0.01f)) { + uv.offset = {offset[0], offset[1]}; + } + + float rotation = uv.rotation; + if (ImGui::DragFloat("Rotation (rad)", &rotation, 0.01f)) { + uv.rotation = rotation; + } + + if (ImGui::Button("Remove UV Transform")) { + registry.remove(selectedEntity); + } + } + + // Script + if (registry.any_of(selectedEntity)) { + auto& script = registry.get(selectedEntity); + ImGui::Separator(); + ImGui::Text("Script"); + ImGui::PushID("ScriptComponent"); + + SyncScriptPathBuffer(selectedEntity); + if (ImGui::InputText("Path", scriptPathBuffer, sizeof(scriptPathBuffer))) { + script.scriptPath = scriptPathBuffer; + script.started = false; + } + + if (ImGui::Checkbox("Enabled", &script.enabled)) { + if (!script.enabled) { + script.started = false; + } + } + + if (ImGui::Button("Remove Script")) { + registry.remove(selectedEntity); + scriptPathEntity = entt::null; + scriptPathBuffer[0] = '\0'; + } + + ImGui::PopID(); + + RenderScriptEditableControls(selectedEntity); + } + + // Camera + if (registry.any_of(selectedEntity)) { + ImGui::PushID("Camera"); + auto& camera = registry.get(selectedEntity); + ImGui::Separator(); + ImGui::Text("Camera"); + + float camPos[3] = {camera.position.x, camera.position.y, camera.position.z}; + float camTarget[3] = {camera.target.x, camera.target.y, camera.target.z}; + float camUp[3] = {camera.up.x, camera.up.y, camera.up.z}; + + if (ImGui::DragFloat3("Position", camPos, 0.1f)) { + camera.position = {camPos[0], camPos[1], camPos[2]}; + } + if (ImGui::DragFloat3("Target", camTarget, 0.1f)) { + camera.target = {camTarget[0], camTarget[1], camTarget[2]}; + } + if (ImGui::DragFloat3("Up", camUp, 0.05f)) { + camera.up = {camUp[0], camUp[1], camUp[2]}; + } + + float fovy = camera.fovy; + if (ImGui::SliderFloat("FOV", &fovy, 1.0f, 179.0f)) { + camera.fovy = fovy; + } + + const char* projectionLabels[] = {"Perspective", "Orthographic"}; + int projectionIndex = (camera.projection == CAMERA_ORTHOGRAPHIC) ? 1 : 0; + if (ImGui::Combo("Projection", &projectionIndex, projectionLabels, 2)) { + camera.projection = (projectionIndex == 1) ? CAMERA_ORTHOGRAPHIC : CAMERA_PERSPECTIVE; + } + + if (ImGui::Button("Look At Origin")) { + camera.target = {0.0f, 0.0f, 0.0f}; + } + ImGui::SameLine(); + if (ImGui::Button("Normalize Up")) { + camera.up = normalizeVector(camera.up, Vector3{0.0f, 1.0f, 0.0f}); + } + ImGui::SameLine(); + if (ImGui::Button("Reset Camera")) { + camera = Camera3DComponent(); + } + + if (ImGui::Button("Remove Camera")) { + registry.remove(selectedEntity); + } + ImGui::PopID(); + } + + // Light + if (registry.any_of(selectedEntity)) { + auto& light = registry.get(selectedEntity); + ImGui::Separator(); + ImGui::Text("Light"); + + float lightDir[3] = {light.direction.x, light.direction.y, light.direction.z}; + if (ImGui::DragFloat3("Direction", lightDir, 0.05f)) { + light.direction = {lightDir[0], lightDir[1], lightDir[2]}; + } + + if (ImGui::Button("Normalize Dir")) { + light.direction = normalizeVector(light.direction, Vector3{0.5f, 0.7f, 0.3f}); + } + ImGui::SameLine(); + if (ImGui::Button("Top")) { + light.direction = {0.0f, 1.0f, 0.0f}; + } + ImGui::SameLine(); + if (ImGui::Button("Angled")) { + light.direction = {0.5f, 0.7f, 0.3f}; + } + ImGui::SameLine(); + if (ImGui::Button("Side")) { + light.direction = {1.0f, 0.0f, 0.0f}; + } + + float lightColor[4] = { + light.color.r / 255.0f, + light.color.g / 255.0f, + light.color.b / 255.0f, + light.color.a / 255.0f + }; + if (ImGui::ColorEdit4("Light Color", lightColor)) { + light.color.r = static_cast(lightColor[0] * 255.0f); + light.color.g = static_cast(lightColor[1] * 255.0f); + light.color.b = static_cast(lightColor[2] * 255.0f); + light.color.a = static_cast(lightColor[3] * 255.0f); + } + + float intensity = light.intensity; + if (ImGui::SliderFloat("Intensity", &intensity, 0.0f, 10.0f)) { + light.intensity = intensity; + } + + if (ImGui::Button("Remove Light")) { + registry.remove(selectedEntity); + } + } + + // Material + if (registry.any_of(selectedEntity)) { + auto& material = registry.get(selectedEntity); + ImGui::Separator(); + ImGui::Text("Material"); + + auto materialKeys = assets.ListMaterialKeys(); + std::sort(materialKeys.begin(), materialKeys.end()); + std::vector materialLabels; + materialLabels.reserve(materialKeys.size() + 1); + materialLabels.push_back(""); + for (const auto& key : materialKeys) { + materialLabels.push_back(key); + } + std::vector materialLabelPtrs; + materialLabelPtrs.reserve(materialLabels.size()); + for (const auto& label : materialLabels) { + materialLabelPtrs.push_back(label.c_str()); + } + + int materialIndex = 0; + std::string currentMaterialKey = assets.GetMaterialKey(material.materialId); + if (!currentMaterialKey.empty()) { + auto it = std::find(materialKeys.begin(), materialKeys.end(), currentMaterialKey); + if (it != materialKeys.end()) { + materialIndex = static_cast(std::distance(materialKeys.begin(), it)) + 1; + } + } + int selectedMaterialIndex = materialIndex; + if (ImGui::Combo("Material Asset", &selectedMaterialIndex, materialLabelPtrs.data(), static_cast(materialLabelPtrs.size()))) { + if (selectedMaterialIndex == 0) { + material.materialId = 0; + } else { + material.materialId = assets.GetMaterialId(materialKeys[selectedMaterialIndex - 1]); + } + } + + int materialId = static_cast(material.materialId); + if (ImGui::InputInt("Material Id", &materialId)) { + material.materialId = materialId < 0 ? 0u : static_cast(materialId); + } + + if (ImGui::Button("Remove Material")) { + registry.remove(selectedEntity); + } + } + + // Render Pass + if (registry.any_of(selectedEntity)) { + auto& pass = registry.get(selectedEntity); + ImGui::Separator(); + ImGui::Text("Render Pass"); + + int order = pass.order; + if (ImGui::InputInt("Order", &order)) { + pass.order = order; + } + ImGui::Checkbox("Depth Test", &pass.depthTest); + ImGui::Checkbox("Depth Write", &pass.depthWrite); + + auto passShaderKeys = assets.ListShaderKeys(); + std::sort(passShaderKeys.begin(), passShaderKeys.end()); + std::vector passShaderLabels; + passShaderLabels.reserve(passShaderKeys.size() + 1); + passShaderLabels.push_back(""); + for (const auto& key : passShaderKeys) { + passShaderLabels.push_back(key); + } + std::vector passShaderLabelPtrs; + passShaderLabelPtrs.reserve(passShaderLabels.size()); + for (const auto& label : passShaderLabels) { + passShaderLabelPtrs.push_back(label.c_str()); + } + + int passShaderIndex = 0; + std::string currentPassShaderKey = assets.GetShaderKey(pass.shaderId); + if (!currentPassShaderKey.empty()) { + auto it = std::find(passShaderKeys.begin(), passShaderKeys.end(), currentPassShaderKey); + if (it != passShaderKeys.end()) { + passShaderIndex = static_cast(std::distance(passShaderKeys.begin(), it)) + 1; + } + } + int selectedPassShaderIndex = passShaderIndex; + if (ImGui::Combo("Pass Shader Asset", &selectedPassShaderIndex, passShaderLabelPtrs.data(), static_cast(passShaderLabelPtrs.size()))) { + if (selectedPassShaderIndex == 0) { + pass.shaderId = 0; + } else { + pass.shaderId = assets.GetShaderId(passShaderKeys[selectedPassShaderIndex - 1]); + } + } + + int shaderId = static_cast(pass.shaderId); + if (ImGui::InputInt("Pass Shader Id", &shaderId)) { + pass.shaderId = shaderId < 0 ? 0u : static_cast(shaderId); + } + + if (ImGui::Button("Remove Render Pass")) { + registry.remove(selectedEntity); + } + } + + ImGui::End(); +} + +static std::string TrimString(const std::string &value) +{ + size_t start = value.find_first_not_of(" \t\r\n"); + if (start == std::string::npos) return std::string(); + size_t end = value.find_last_not_of(" \t\r\n"); + return value.substr(start, end - start + 1); +} + +static std::string UnquoteString(const std::string &value) +{ + if (value.size() >= 2 && value.front() == '"' && value.back() == '"') { + return value.substr(1, value.size() - 2); + } + return value; +} + +static bool ExtractTagContent(const std::string &line, const std::string &tag, std::string &out) +{ + std::string needle = "[" + tag + "["; + size_t start = line.find(needle); + if (start == std::string::npos) return false; + start += needle.size(); + size_t end = line.find("]]", start); + if (end == std::string::npos) return false; + out = line.substr(start, end - start); + return true; +} + +static std::vector SplitOptions(const std::string &value) +{ + std::vector parts; + std::stringstream ss(value); + std::string item; + while (std::getline(ss, item, ',')) { + parts.push_back(TrimString(item)); + } + return parts; +} + +static bool TryParseRange(const std::string &value, float &outMin, float &outMax) +{ + size_t comma = value.find(','); + if (comma == std::string::npos) return false; + std::string left = TrimString(value.substr(0, comma)); + std::string right = TrimString(value.substr(comma + 1)); + try { + outMin = std::stof(left); + outMax = std::stof(right); + return true; + } catch (...) { + return false; + } +} + +GuiManager::ScriptEditableCache &GuiManager::GetOrParseEditableCache(const std::string &path, const std::string &className) +{ + std::string key = path + "::" + className; + auto &cache = scriptEditableCache[key]; + cache.className = className; + + bool needsParse = true; + std::int64_t writeTime = PhysFSManager::GetModTime(path); + if (writeTime != 0 && cache.hasWriteTime && cache.lastWriteTime == writeTime) { + needsParse = false; + } else { + cache.lastWriteTime = writeTime; + cache.hasWriteTime = (writeTime != 0); + } + + if (!needsParse) return cache; + + cache.fields.clear(); + + std::string contents; + if (!PhysFSManager::ReadTextFile(path, contents)) { + return cache; + } + + std::istringstream file(contents); + std::string line; + bool inClass = false; + int braceDepth = 0; + + while (std::getline(file, line)) { + std::string trimmed = TrimString(line); + if (!inClass) { + if (trimmed.find("class " + className) != std::string::npos) { + inClass = true; + for (char c : trimmed) { + if (c == '{') braceDepth++; + } + } + continue; + } + + for (char c : line) { + if (c == '{') braceDepth++; + if (c == '}') braceDepth--; + } + + if (braceDepth <= 0) { + inClass = false; + continue; + } + + if (line.find("[editable]") == std::string::npos) continue; + + ScriptEditableFieldMeta meta; + meta.hasRange = false; + meta.rangeMin = 0.0f; + meta.rangeMax = 1.0f; + meta.group.clear(); + meta.description.clear(); + + std::string rangeContent; + if (ExtractTagContent(line, "range", rangeContent)) { + float minVal = 0.0f; + float maxVal = 1.0f; + if (TryParseRange(rangeContent, minVal, maxVal)) { + meta.hasRange = true; + meta.rangeMin = minVal; + meta.rangeMax = maxVal; + } + } + + std::string groupContent; + if (ExtractTagContent(line, "group", groupContent)) { + meta.group = UnquoteString(TrimString(groupContent)); + } + + std::string descContent; + if (ExtractTagContent(line, "desc", descContent)) { + meta.description = UnquoteString(TrimString(descContent)); + } + + std::string optionsContent; + if (ExtractTagContent(line, "options", optionsContent)) { + meta.options = SplitOptions(optionsContent); + } + + size_t lastTag = line.find_last_of(']'); + if (lastTag == std::string::npos) continue; + std::string declaration = TrimString(line.substr(lastTag + 1)); + if (declaration.empty()) continue; + + std::stringstream declStream(declaration); + declStream >> meta.type; + std::string nameToken; + declStream >> nameToken; + if (meta.type.empty() || nameToken.empty()) continue; + + size_t equalPos = nameToken.find('='); + if (equalPos != std::string::npos) { + nameToken = nameToken.substr(0, equalPos); + } + if (!nameToken.empty() && nameToken.back() == ';') { + nameToken.pop_back(); + } + meta.name = TrimString(nameToken); + if (!meta.name.empty()) { + cache.fields.push_back(meta); + } + } + + return cache; +} + +void GuiManager::RenderScriptEditableControls(entt::entity entity) +{ + auto ®istry = app->GetRegistry(); + auto &script = registry.get(entity); + if (!script.enabled || script.scriptPath.empty() || !script.scriptInstance) return; + + auto *obj = reinterpret_cast(script.scriptInstance); + if (!obj) return; + + // If the script instance changed (hot reload or reassign), drop staged values + auto instIt = scriptEditableInstancePtr.find(entity); + if (instIt == scriptEditableInstancePtr.end() || instIt->second != script.scriptInstance) { + scriptEditableStaged.erase(entity); + scriptEditableInstancePtr[entity] = script.scriptInstance; + } + + asITypeInfo *objType = obj->GetObjectType(); + if (!objType) return; + asIScriptEngine *engine = obj->GetEngine(); + if (!engine) return; + + const char *className = objType->GetName(); + if (!className) return; + + auto &cache = GetOrParseEditableCache(script.scriptPath, className); + if (cache.fields.empty()) return; + + ImGui::Separator(); + ImGui::Text("Editable"); + ImGui::PushID("ScriptEditable"); + + int floatTypeId = engine->GetTypeIdByDecl("float"); + int intTypeId = engine->GetTypeIdByDecl("int"); + int boolTypeId = engine->GetTypeIdByDecl("bool"); + int stringTypeId = engine->GetTypeIdByDecl("string"); + + auto &stagedMap = scriptEditableStaged[entity]; + + std::string currentGroup; + bool groupOpen = true; + for (const auto &field : cache.fields) { + if (field.group != currentGroup) { + currentGroup = field.group; + if (!currentGroup.empty()) { + groupOpen = ImGui::CollapsingHeader(currentGroup.c_str(), ImGuiTreeNodeFlags_DefaultOpen); + } else { + groupOpen = true; + } + } + if (!groupOpen) { + continue; + } + int propertyIndex = -1; + int propertyTypeId = 0; + const int propertyCount = static_cast(obj->GetPropertyCount()); + for (int i = 0; i < propertyCount; ++i) { + const char *propName = obj->GetPropertyName(i); + if (propName && field.name == propName) { + propertyIndex = i; + propertyTypeId = obj->GetPropertyTypeId(i); + break; + } + } + if (propertyIndex < 0) continue; + + void *propAddr = obj->GetAddressOfProperty(propertyIndex); + if (!propAddr) continue; + + ScriptEditableValue &staged = stagedMap[field.name]; + if (!staged.hasValue) { + if (propertyTypeId == floatTypeId) { + staged.kind = ScriptValueKind::Float; + staged.f = *reinterpret_cast(propAddr); + staged.hasValue = true; + staged.baselineF = staged.f; + staged.hasBaseline = true; + } else if (propertyTypeId == intTypeId) { + staged.kind = ScriptValueKind::Int; + staged.i = *reinterpret_cast(propAddr); + staged.hasValue = true; + staged.baselineI = staged.i; + staged.hasBaseline = true; + } else if (propertyTypeId == boolTypeId) { + staged.kind = ScriptValueKind::Bool; + staged.b = *reinterpret_cast(propAddr); + staged.hasValue = true; + staged.baselineB = staged.b; + staged.hasBaseline = true; + } else if (propertyTypeId == stringTypeId) { + staged.kind = ScriptValueKind::String; + staged.s = *reinterpret_cast(propAddr); + staged.hasValue = true; + staged.baselineS = staged.s; + staged.hasBaseline = true; + } else { + continue; + } + } + + ImGui::PushID(field.name.c_str()); + bool changed = false; + if (ImGui::SmallButton(ICON_FA_ROTATE_LEFT)) { + if (staged.kind == ScriptValueKind::Float && staged.hasBaseline) { + staged.f = staged.baselineF; + } else if (staged.kind == ScriptValueKind::Int && staged.hasBaseline) { + staged.i = staged.baselineI; + } else if (staged.kind == ScriptValueKind::Bool && staged.hasBaseline) { + staged.b = staged.baselineB; + } else if (staged.kind == ScriptValueKind::String && staged.hasBaseline) { + staged.s = staged.baselineS; + } + changed = true; + } + ImGui::SameLine(); + + if (staged.kind == ScriptValueKind::Float) { + if (!field.options.empty()) { + int currentIndex = 0; + float currentValue = staged.f; + int maxIndex = static_cast(field.options.size()) - 1; + if (maxIndex < 0) maxIndex = 0; + currentIndex = static_cast(currentValue); + currentIndex = std::clamp(currentIndex, 0, maxIndex); + if (ImGui::SliderInt(field.name.c_str(), ¤tIndex, 0, maxIndex)) { + staged.f = static_cast(currentIndex); + changed = true; + } + } else if (field.hasRange) { + if (ImGui::SliderFloat(field.name.c_str(), &staged.f, field.rangeMin, field.rangeMax)) { + changed = true; + } + } else { + if (ImGui::DragFloat(field.name.c_str(), &staged.f, 0.1f)) { + changed = true; + } + } + } else if (staged.kind == ScriptValueKind::Int) { + if (!field.options.empty()) { + int currentIndex = staged.i; + int maxIndex = static_cast(field.options.size()) - 1; + if (maxIndex < 0) maxIndex = 0; + currentIndex = std::clamp(currentIndex, 0, maxIndex); + if (ImGui::Combo(field.name.c_str(), ¤tIndex, [](void *data, int idx, const char **outText) { + auto *opts = reinterpret_cast *>(data); + if (idx < 0 || idx >= static_cast(opts->size())) return false; + *outText = (*opts)[idx].c_str(); + return true; + }, (void *)&field.options, static_cast(field.options.size()))) { + staged.i = currentIndex; + changed = true; + } + } else if (field.hasRange) { + if (ImGui::SliderInt(field.name.c_str(), &staged.i, static_cast(field.rangeMin), static_cast(field.rangeMax))) { + changed = true; + } + } else { + if (ImGui::DragInt(field.name.c_str(), &staged.i, 1.0f)) { + changed = true; + } + } + } else if (staged.kind == ScriptValueKind::Bool) { + if (ImGui::Checkbox(field.name.c_str(), &staged.b)) { + changed = true; + } + } else if (staged.kind == ScriptValueKind::String) { + if (!field.options.empty()) { + int currentIndex = 0; + for (size_t i = 0; i < field.options.size(); ++i) { + if (field.options[i] == staged.s) { + currentIndex = static_cast(i); + break; + } + } + if (ImGui::Combo(field.name.c_str(), ¤tIndex, [](void *data, int idx, const char **outText) { + auto *opts = reinterpret_cast *>(data); + if (idx < 0 || idx >= static_cast(opts->size())) return false; + *outText = (*opts)[idx].c_str(); + return true; + }, (void *)&field.options, static_cast(field.options.size()))) { + if (currentIndex >= 0 && currentIndex < static_cast(field.options.size())) { + staged.s = field.options[currentIndex]; + changed = true; + } + } + } else { + char buffer[256]; + std::snprintf(buffer, sizeof(buffer), "%s", staged.s.c_str()); + if (ImGui::InputText(field.name.c_str(), buffer, sizeof(buffer))) { + staged.s = buffer; + changed = true; + } + } + } + + if (!field.description.empty() && ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::TextUnformatted(field.description.c_str()); + ImGui::EndTooltip(); + } + + if (changed) { + if (propertyTypeId == floatTypeId && staged.kind == ScriptValueKind::Float) { + *reinterpret_cast(propAddr) = staged.f; + script.scriptOverrides[field.name] = std::to_string(staged.f); + } else if (propertyTypeId == intTypeId && staged.kind == ScriptValueKind::Int) { + *reinterpret_cast(propAddr) = staged.i; + script.scriptOverrides[field.name] = std::to_string(staged.i); + } else if (propertyTypeId == boolTypeId && staged.kind == ScriptValueKind::Bool) { + *reinterpret_cast(propAddr) = staged.b; + script.scriptOverrides[field.name] = staged.b ? "true" : "false"; + } else if (propertyTypeId == stringTypeId && staged.kind == ScriptValueKind::String) { + *reinterpret_cast(propAddr) = staged.s; + script.scriptOverrides[field.name] = staged.s; + } + + // If reset was clicked and we returned to baseline, clear overrides + if (staged.hasBaseline) { + bool atBaseline = false; + if (staged.kind == ScriptValueKind::Float) atBaseline = (staged.f == staged.baselineF); + else if (staged.kind == ScriptValueKind::Int) atBaseline = (staged.i == staged.baselineI); + else if (staged.kind == ScriptValueKind::Bool) atBaseline = (staged.b == staged.baselineB); + else if (staged.kind == ScriptValueKind::String) atBaseline = (staged.s == staged.baselineS); + if (atBaseline) { + script.scriptOverrides.erase(field.name); + } + } + } + ImGui::PopID(); + } + ImGui::PopID(); +} + +void GuiManager::RenderErrorBanner() +{ + if (!app || !app->HasScriptCompilationError()) + return; + + ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f)); + ImGui::SetNextWindowSize(ImVec2(ImGui::GetIO().DisplaySize.x, 0.0f)); + ImGuiWindowFlags flags = ImGuiWindowFlags_NoDecoration | + ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoSavedSettings; + ImGui::Begin("PersistentBanner", nullptr, flags); + ImGui::TextColored(ImVec4(1, 0.4f, 0.4f, 1), ICON_FA_TRIANGLE_EXCLAMATION " Script Compilation Error - running last working version"); + ImGui::End(); +} + +void GuiManager::RenderNotifications() +{ + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.f); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.f); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.10f, 0.10f, 0.10f, 1.00f)); + + ImGui::RenderNotifications(); + + ImGui::PopStyleVar(2); + ImGui::PopStyleColor(1); +} +void GuiManager::SetTheme() +{ + + ImGuiIO &io = ImGui::GetIO(); + + io.Fonts->Clear(); + + // Don't clear; just add new fonts and rebuild + io.Fonts->AddFontFromFileTTF("fonts/Inter-Regular.ttf", 16); // index 0 (replaces default) + + // Re-add FontAwesome as merge into default (index 0) + float iconFontSize = 16.0f * 2.0f / 3.0f; + static constexpr ImWchar iconsRanges[] = {ICON_MIN_FA, ICON_MAX_16_FA, 0}; + ImFontConfig iconsConfig; + iconsConfig.MergeMode = true; + iconsConfig.PixelSnapH = true; + iconsConfig.GlyphMinAdvanceX = iconFontSize; + io.Fonts->AddFontFromMemoryCompressedTTF(fa_solid_900_compressed_data, + fa_solid_900_compressed_size, + iconFontSize, &iconsConfig, iconsRanges); + io.Fonts->AddFontFromFileTTF("fonts/JetBrainsMono-Regular.ttf", 16); + io.Fonts->Build(); + + ImVec4 *colors = ImGui::GetStyle().Colors; + colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); + colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); + colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.10f, 1.00f); + colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_PopupBg] = ImVec4(0.19f, 0.19f, 0.19f, 0.92f); + colors[ImGuiCol_Border] = ImVec4(0.19f, 0.19f, 0.19f, 0.29f); + colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.24f); + colors[ImGuiCol_FrameBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); + colors[ImGuiCol_FrameBgHovered] = ImVec4(0.19f, 0.19f, 0.19f, 0.54f); + colors[ImGuiCol_FrameBgActive] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); + colors[ImGuiCol_TitleBg] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_TitleBgActive] = ImVec4(0.06f, 0.06f, 0.06f, 1.00f); + colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); + colors[ImGuiCol_ScrollbarBg] = ImVec4(0.05f, 0.05f, 0.05f, 0.54f); + colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); + colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.40f, 0.40f, 0.40f, 0.54f); + colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); + colors[ImGuiCol_CheckMark] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); + colors[ImGuiCol_SliderGrab] = ImVec4(0.34f, 0.34f, 0.34f, 0.54f); + colors[ImGuiCol_SliderGrabActive] = ImVec4(0.56f, 0.56f, 0.56f, 0.54f); + colors[ImGuiCol_Button] = ImVec4(0.10f, 0.58f, 0.30f, 0.90f); + colors[ImGuiCol_ButtonHovered] = ImVec4(0.12f, 0.70f, 0.36f, 1.00f); + colors[ImGuiCol_ButtonActive] = ImVec4(0.08f, 0.46f, 0.24f, 1.00f); + colors[ImGuiCol_Header] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_HeaderHovered] = ImVec4(0.00f, 0.00f, 0.00f, 0.36f); + colors[ImGuiCol_HeaderActive] = ImVec4(0.20f, 0.22f, 0.23f, 0.33f); + colors[ImGuiCol_Separator] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); + colors[ImGuiCol_SeparatorHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); + colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); + colors[ImGuiCol_ResizeGrip] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); + colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.44f, 0.44f, 0.44f, 0.29f); + colors[ImGuiCol_ResizeGripActive] = ImVec4(0.40f, 0.44f, 0.47f, 1.00f); + colors[ImGuiCol_Tab] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TabHovered] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); + colors[ImGuiCol_TabActive] = ImVec4(0.20f, 0.20f, 0.20f, 0.36f); + colors[ImGuiCol_TabUnfocused] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); + colors[ImGuiCol_DockingPreview] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); + colors[ImGuiCol_DockingEmptyBg] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_TableHeaderBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TableBorderStrong] = ImVec4(0.00f, 0.00f, 0.00f, 0.52f); + colors[ImGuiCol_TableBorderLight] = ImVec4(0.28f, 0.28f, 0.28f, 0.29f); + colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); + colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); + colors[ImGuiCol_TextSelectedBg] = ImVec4(0.20f, 0.22f, 0.23f, 1.00f); + colors[ImGuiCol_DragDropTarget] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); + colors[ImGuiCol_NavHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 1.00f); + colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 0.00f, 0.00f, 0.70f); + colors[ImGuiCol_NavWindowingDimBg] = ImVec4(1.00f, 0.00f, 0.00f, 0.20f); + colors[ImGuiCol_ModalWindowDimBg] = ImVec4(1.00f, 0.00f, 0.00f, 0.35f); + colors[ImGuiCol_CheckMark] = ImVec4(0.33f, 0.67f, 0.86f, 1.00f); + + ImGuiStyle &style = ImGui::GetStyle(); + style.WindowPadding = ImVec2(8.00f, 8.00f); + style.FramePadding = ImVec2(5.00f, 2.00f); + style.CellPadding = ImVec2(6.00f, 6.00f); + style.ItemSpacing = ImVec2(6.00f, 6.00f); + style.ItemInnerSpacing = ImVec2(6.00f, 6.00f); + style.TouchExtraPadding = ImVec2(0.00f, 0.00f); + style.IndentSpacing = 25; + style.ScrollbarSize = 15; + style.GrabMinSize = 10; + style.WindowBorderSize = 1; + style.ChildBorderSize = 1; + style.PopupBorderSize = 1; + style.FrameBorderSize = 1; + style.TabBorderSize = 1; + style.WindowRounding = 7; + style.ChildRounding = 4; + style.FrameRounding = 3; + style.PopupRounding = 4; + style.ScrollbarRounding = 9; + style.GrabRounding = 3; + style.LogSliderDeadzone = 4; + style.TabRounding = 4; +} diff --git a/src/gui/ImViewGuizmoIntegration.cpp b/src/gui/ImViewGuizmoIntegration.cpp new file mode 100644 index 0000000..48db19f --- /dev/null +++ b/src/gui/ImViewGuizmoIntegration.cpp @@ -0,0 +1 @@ +#include "ImViewGuizmoRaymath.h" diff --git a/src/LogWindow.cpp b/src/gui/LogWindow.cpp similarity index 90% rename from src/LogWindow.cpp rename to src/gui/LogWindow.cpp index 1f93770..127f3d1 100644 --- a/src/LogWindow.cpp +++ b/src/gui/LogWindow.cpp @@ -1,4 +1,4 @@ -#include "LogWindow.h" +#include "gui/LogWindow.h" #include "log.h" void LogWindow::Initialize() @@ -13,6 +13,8 @@ void LogWindow::Render(bool& showLogWindow) return; ImGui::Begin("Log Viewer"); + ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); // Assuming mono font is at index 1 + ImGui::BeginChild("LogText", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar); for (const auto& entry : logEntries) @@ -24,6 +26,7 @@ void LogWindow::Render(bool& showLogWindow) ImGui::SetScrollHereY(1.0f); ImGui::EndChild(); + ImGui::PopFont(); ImGui::End(); } diff --git a/src/log/log.h b/src/log/log.h index b02be28..d0f134e 100644 --- a/src/log/log.h +++ b/src/log/log.h @@ -58,4 +58,4 @@ void raylib_log(int msgType, const char *text, va_list args); } #endif -#endif \ No newline at end of file +#endif diff --git a/src/scripting/DrawBindings.cpp b/src/scripting/DrawBindings.cpp new file mode 100644 index 0000000..6fc14a9 --- /dev/null +++ b/src/scripting/DrawBindings.cpp @@ -0,0 +1,72 @@ +#include "scripting/DrawBindings.h" +#include "raylib.h" +#include +#include +#include + +Color ColorFromUInt(unsigned int c) +{ + Color col; + col.r = (c >> 24) & 0xFF; + col.g = (c >> 16) & 0xFF; + col.b = (c >> 8) & 0xFF; + col.a = c & 0xFF; + return col; +} + +void AS_DrawPixel(int x, int y, unsigned int color) +{ + DrawPixel(x, y, ColorFromUInt(color)); +} + +void AS_DrawLine(int startX, int startY, int endX, int endY, unsigned int color) +{ + DrawLine(startX, startY, endX, endY, ColorFromUInt(color)); +} + +void AS_DrawCircle(int centerX, int centerY, float radius, unsigned int color) +{ + DrawCircle(centerX, centerY, radius, ColorFromUInt(color)); +} +void AS_DrawText(const std::string &text, int x, int y, int fontSize, unsigned int color) +{ + DrawText(text.c_str(), x, y, fontSize, ColorFromUInt(color)); +} + +void AS_DrawRectangle(int x, int y, int width, int height, unsigned int color) +{ + DrawRectangle(x, y, width, height, ColorFromUInt(color)); +} + +void RegisterDrawBindings(asIScriptEngine *engine) +{ + int r; + + engine->SetDefaultNamespace("Draw"); + + r = engine->RegisterGlobalFunction("void Pixel(int, int, uint)", + asFUNCTION(AS_DrawPixel), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void Line(int, int, int, int, uint)", + asFUNCTION(AS_DrawLine), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void Circle(int, int, float, uint)", + asFUNCTION(AS_DrawCircle), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Text(const string &in, int, int, int, uint)", + asFUNCTION(AS_DrawText), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void FPS(int, int)", + asFUNCTION(DrawFPS), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Rectangle(int, int, int, int, uint)", + asFUNCTION(AS_DrawRectangle), asCALL_CDECL); + assert(r >= 0); + + + + engine->SetDefaultNamespace(""); +} diff --git a/src/scripting/ECSBindings.cpp b/src/scripting/ECSBindings.cpp new file mode 100644 index 0000000..0737509 --- /dev/null +++ b/src/scripting/ECSBindings.cpp @@ -0,0 +1,777 @@ +#include "scripting/ECSBindings.h" +#include "ECSComponents.h" +#include "SceneGraph.h" +#include +#include +#include + +// Global registry pointer for script access +static entt::registry* registry = nullptr; + +void SetGlobalRegistry(entt::registry* reg) { + registry = reg; +} + +// === Entity Management === + +uint32_t AS_CreateEntity() { + if (!registry) return 0; + return (uint32_t)registry->create(); +} + +void AS_DestroyEntity(uint32_t entity) { + if (!registry) return; + entt::entity e = (entt::entity)entity; + DetachFromParent(*registry, e); + DetachChildren(*registry, e); + registry->destroy(e); +} + +bool AS_IsEntityValid(uint32_t entity) { + if (!registry) return false; + return registry->valid((entt::entity)entity); +} + +// === Transform Component === + +void AS_AddTransform(uint32_t entity, float x, float y, float z) { + if (!registry) return; + registry->emplace((entt::entity)entity, + Vector3{x, y, z}, Vector3{1.0f, 1.0f, 1.0f}, 0.0f); +} + +void AS_SetPosition(uint32_t entity, float x, float y, float z) { + if (!registry) return; + if (auto* t = registry->try_get((entt::entity)entity)) { + t->position = {x, y, z}; + } +} + +void AS_GetPosition(uint32_t entity, float* x, float* y, float* z) { + if (!registry || !x || !y || !z) return; + if (auto* t = registry->try_get((entt::entity)entity)) { + *x = t->position.x; + *y = t->position.y; + *z = t->position.z; + } +} + +void AS_SetScale(uint32_t entity, float x, float y, float z) { + if (!registry) return; + if (auto* t = registry->try_get((entt::entity)entity)) { + t->scale = {x, y, z}; + } +} + +void AS_SetRotation(uint32_t entity, float rotation) { + if (!registry) return; + if (auto* t = registry->try_get((entt::entity)entity)) { + t->rotation.y = rotation; + } +} + +float AS_GetRotation(uint32_t entity) { + if (!registry) return 0.0f; + if (auto* t = registry->try_get((entt::entity)entity)) { + return t->rotation.y; + } + return 0.0f; +} + +void AS_SetRotationEuler(uint32_t entity, float x, float y, float z) { + if (!registry) return; + if (auto* t = registry->try_get((entt::entity)entity)) { + t->rotation = {x, y, z}; + } +} + +void AS_GetRotationEuler(uint32_t entity, float* x, float* y, float* z) { + if (!registry || !x || !y || !z) return; + if (auto* t = registry->try_get((entt::entity)entity)) { + *x = t->rotation.x; + *y = t->rotation.y; + *z = t->rotation.z; + } +} + +bool AS_HasTransform(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveTransform(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === Velocity Component === + +void AS_AddVelocity(uint32_t entity, float vx, float vy, float vz, float angular) { + if (!registry) return; + registry->emplace((entt::entity)entity, Vector3{vx, vy, vz}, angular); +} + +void AS_SetVelocity(uint32_t entity, float vx, float vy, float vz) { + if (!registry) return; + if (auto* v = registry->try_get((entt::entity)entity)) { + v->linear = {vx, vy, vz}; + } +} + +void AS_SetAngularVelocity(uint32_t entity, float angular) { + if (!registry) return; + if (auto* v = registry->try_get((entt::entity)entity)) { + v->angular = angular; + } +} + +bool AS_HasVelocity(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveVelocity(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === ModelRenderer Component === + +void AS_AddModelRenderer(uint32_t entity, int modelId, uint32_t color, float outlineSize) { + if (!registry) return; + Color col; + col.r = (color >> 24) & 0xFF; + col.g = (color >> 16) & 0xFF; + col.b = (color >> 8) & 0xFF; + col.a = color & 0xFF; + registry->emplace((entt::entity)entity, modelId, col, outlineSize); +} + +void AS_SetModelRendererColor(uint32_t entity, uint32_t color) { + if (!registry) return; + if (auto* renderer = registry->try_get((entt::entity)entity)) { + renderer->color.r = (color >> 24) & 0xFF; + renderer->color.g = (color >> 16) & 0xFF; + renderer->color.b = (color >> 8) & 0xFF; + renderer->color.a = color & 0xFF; + } +} + +void AS_SetModelRendererModel(uint32_t entity, int modelId) { + if (!registry) return; + if (auto* renderer = registry->try_get((entt::entity)entity)) { + renderer->modelId = modelId; + } +} + +void AS_SetModelRendererOutline(uint32_t entity, float outlineSize) { + if (!registry) return; + if (auto* renderer = registry->try_get((entt::entity)entity)) { + renderer->outlineSize = outlineSize; + } +} + +void AS_SetModelRendererShader(uint32_t entity, unsigned int shaderId) { + if (!registry) return; + if (auto* renderer = registry->try_get((entt::entity)entity)) { + renderer->shaderId = shaderId; + } +} + +bool AS_HasModelRenderer(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveModelRenderer(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === Texture Component === + +void AS_AddTexture(uint32_t entity, unsigned int textureId) { + if (!registry) return; + registry->emplace((entt::entity)entity, textureId); +} + +void AS_SetTextureId(uint32_t entity, unsigned int textureId) { + if (!registry) return; + if (auto* t = registry->try_get((entt::entity)entity)) { + t->textureId = textureId; + } +} + +unsigned int AS_GetTextureId(uint32_t entity) { + if (!registry) return 0; + if (auto* t = registry->try_get((entt::entity)entity)) { + return t->textureId; + } + return 0; +} + +bool AS_HasTexture(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveTexture(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === UV Transform Component === + +void AS_AddUVTransform(uint32_t entity, float scaleX, float scaleY, float offsetX, float offsetY, float rotation) { + if (!registry) return; + registry->emplace((entt::entity)entity, + Vector2{scaleX, scaleY}, Vector2{offsetX, offsetY}, rotation); +} + +void AS_SetUVScale(uint32_t entity, float scaleX, float scaleY) { + if (!registry) return; + if (auto* uv = registry->try_get((entt::entity)entity)) { + uv->scale = {scaleX, scaleY}; + } +} + +void AS_SetUVOffset(uint32_t entity, float offsetX, float offsetY) { + if (!registry) return; + if (auto* uv = registry->try_get((entt::entity)entity)) { + uv->offset = {offsetX, offsetY}; + } +} + +void AS_SetUVRotation(uint32_t entity, float rotation) { + if (!registry) return; + if (auto* uv = registry->try_get((entt::entity)entity)) { + uv->rotation = rotation; + } +} + +bool AS_HasUVTransform(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveUVTransform(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === Tag Component === + +void AS_AddTag(uint32_t entity, const std::string& name) { + if (!registry) return; + registry->emplace((entt::entity)entity, name); +} + +std::string AS_GetTag(uint32_t entity) { + if (!registry) return ""; + if (auto* t = registry->try_get((entt::entity)entity)) { + return t->name; + } + return ""; +} + +void AS_SetTag(uint32_t entity, const std::string& name) { + if (!registry) return; + if (auto* t = registry->try_get((entt::entity)entity)) { + t->name = name; + } +} + +bool AS_HasTag(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveTag(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === Hierarchy Component === + +void AS_SetParent(uint32_t child, uint32_t parent) { + if (!registry) return; + entt::entity childEntity = (entt::entity)child; + entt::entity parentEntity = (entt::entity)parent; + + if (!registry->valid(childEntity)) return; + if (parent == 0 || !registry->valid(parentEntity)) { + DetachFromParent(*registry, childEntity); + return; + } + + AttachChild(*registry, childEntity, parentEntity); +} + +void AS_ClearParent(uint32_t child) { + if (!registry) return; + entt::entity childEntity = (entt::entity)child; + DetachFromParent(*registry, childEntity); +} + +uint32_t AS_GetParent(uint32_t child) { + if (!registry) return 0; + if (auto* h = registry->try_get((entt::entity)child)) { + return static_cast(h->parent); + } + return 0; +} + +bool AS_HasParent(uint32_t child) { + if (!registry) return false; + if (auto* h = registry->try_get((entt::entity)child)) { + return h->parent != entt::null; + } + return false; +} + +// === Camera3D Component === + +void AS_AddCamera3D(uint32_t entity, float px, float py, float pz, float tx, float ty, float tz, float fovy) { + if (!registry) return; + Camera3DComponent cam; + cam.position = {px, py, pz}; + cam.target = {tx, ty, tz}; + cam.up = {0.0f, 1.0f, 0.0f}; + cam.fovy = fovy; + cam.projection = 0; // CAMERA_PERSPECTIVE + registry->emplace((entt::entity)entity, cam); +} + +void AS_SetCameraPosition(uint32_t entity, float x, float y, float z) { + if (!registry) return; + if (auto* c = registry->try_get((entt::entity)entity)) { + c->position = {x, y, z}; + } +} + +void AS_SetCameraTarget(uint32_t entity, float x, float y, float z) { + if (!registry) return; + if (auto* c = registry->try_get((entt::entity)entity)) { + c->target = {x, y, z}; + } +} + +void AS_SetCameraFovy(uint32_t entity, float fovy) { + if (!registry) return; + if (auto* c = registry->try_get((entt::entity)entity)) { + c->fovy = fovy; + } +} + +bool AS_HasCamera3D(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveCamera3D(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === Light Component === + +void AS_AddLight(uint32_t entity, float dx, float dy, float dz, float intensity) { + if (!registry) return; + LightComponent light; + light.direction = {dx, dy, dz}; + light.color = WHITE; + light.intensity = intensity; + registry->emplace((entt::entity)entity, light); +} + +void AS_SetLightDirection(uint32_t entity, float x, float y, float z) { + if (!registry) return; + if (auto* l = registry->try_get((entt::entity)entity)) { + l->direction = {x, y, z}; + } +} + +void AS_SetLightIntensity(uint32_t entity, float intensity) { + if (!registry) return; + if (auto* l = registry->try_get((entt::entity)entity)) { + l->intensity = intensity; + } +} + +void AS_SetLightColor(uint32_t entity, uint32_t color) { + if (!registry) return; + if (auto* l = registry->try_get((entt::entity)entity)) { + l->color.r = (color >> 24) & 0xFF; + l->color.g = (color >> 16) & 0xFF; + l->color.b = (color >> 8) & 0xFF; + l->color.a = color & 0xFF; + } +} + +bool AS_HasLight(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveLight(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === Material Component === + +void AS_AddMaterial(uint32_t entity, unsigned int materialId) { + if (!registry) return; + registry->emplace((entt::entity)entity, materialId); +} + +void AS_SetMaterialId(uint32_t entity, unsigned int materialId) { + if (!registry) return; + if (auto* m = registry->try_get((entt::entity)entity)) { + m->materialId = materialId; + } +} + +unsigned int AS_GetMaterialId(uint32_t entity) { + if (!registry) return 0; + if (auto* m = registry->try_get((entt::entity)entity)) { + return m->materialId; + } + return 0; +} + +bool AS_HasMaterial(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveMaterialComponent(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === RenderPass Component === + +void AS_AddRenderPass(uint32_t entity, int order, bool depthTest, bool depthWrite, unsigned int shaderId) { + if (!registry) return; + RenderPassComponent pass; + pass.order = order; + pass.depthTest = depthTest; + pass.depthWrite = depthWrite; + pass.shaderId = shaderId; + registry->emplace((entt::entity)entity, pass); +} + +void AS_SetRenderPassOrder(uint32_t entity, int order) { + if (!registry) return; + if (auto* p = registry->try_get((entt::entity)entity)) { + p->order = order; + } +} + +void AS_SetRenderPassShader(uint32_t entity, unsigned int shaderId) { + if (!registry) return; + if (auto* p = registry->try_get((entt::entity)entity)) { + p->shaderId = shaderId; + } +} + +bool AS_HasRenderPass(uint32_t entity) { + if (!registry) return false; + return registry->all_of((entt::entity)entity); +} + +void AS_RemoveRenderPass(uint32_t entity) { + if (!registry) return; + registry->remove((entt::entity)entity); +} + +// === Registration === + +void RegisterECSBindings(asIScriptEngine *engine) { + int r; + + engine->SetDefaultNamespace("ECS"); + + // Entity management + r = engine->RegisterGlobalFunction("uint CreateEntity()", + asFUNCTION(AS_CreateEntity), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void DestroyEntity(uint)", + asFUNCTION(AS_DestroyEntity), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool IsValid(uint)", + asFUNCTION(AS_IsEntityValid), asCALL_CDECL); + assert(r >= 0); + + // Transform component + r = engine->RegisterGlobalFunction("void AddTransform(uint, float, float, float)", + asFUNCTION(AS_AddTransform), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetPosition(uint, float, float, float)", + asFUNCTION(AS_SetPosition), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void GetPosition(uint, float &out, float &out, float &out)", + asFUNCTION(AS_GetPosition), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetScale(uint, float, float, float)", + asFUNCTION(AS_SetScale), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetRotation(uint, float)", + asFUNCTION(AS_SetRotation), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetRotationEuler(uint, float, float, float)", + asFUNCTION(AS_SetRotationEuler), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float GetRotation(uint)", + asFUNCTION(AS_GetRotation), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void GetRotationEuler(uint, float &out, float &out, float &out)", + asFUNCTION(AS_GetRotationEuler), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasTransform(uint)", + asFUNCTION(AS_HasTransform), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveTransform(uint)", + asFUNCTION(AS_RemoveTransform), asCALL_CDECL); + assert(r >= 0); + + // Velocity component + r = engine->RegisterGlobalFunction("void AddVelocity(uint, float, float, float, float = 0.0f)", + asFUNCTION(AS_AddVelocity), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetVelocity(uint, float, float, float)", + asFUNCTION(AS_SetVelocity), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetAngularVelocity(uint, float)", + asFUNCTION(AS_SetAngularVelocity), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasVelocity(uint)", + asFUNCTION(AS_HasVelocity), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveVelocity(uint)", + asFUNCTION(AS_RemoveVelocity), asCALL_CDECL); + assert(r >= 0); + + // ModelRenderer component + r = engine->RegisterGlobalFunction("void AddModelRenderer(uint, int, uint, float = 0.0f)", + asFUNCTION(AS_AddModelRenderer), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetModelRendererColor(uint, uint)", + asFUNCTION(AS_SetModelRendererColor), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetModelRendererModel(uint, int)", + asFUNCTION(AS_SetModelRendererModel), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetModelRendererOutline(uint, float)", + asFUNCTION(AS_SetModelRendererOutline), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetModelRendererShader(uint, uint)", + asFUNCTION(AS_SetModelRendererShader), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasModelRenderer(uint)", + asFUNCTION(AS_HasModelRenderer), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveModelRenderer(uint)", + asFUNCTION(AS_RemoveModelRenderer), asCALL_CDECL); + assert(r >= 0); + + // Texture component + r = engine->RegisterGlobalFunction("void AddTexture(uint, uint)", + asFUNCTION(AS_AddTexture), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetTextureId(uint, uint)", + asFUNCTION(AS_SetTextureId), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint GetTextureId(uint)", + asFUNCTION(AS_GetTextureId), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasTexture(uint)", + asFUNCTION(AS_HasTexture), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveTexture(uint)", + asFUNCTION(AS_RemoveTexture), asCALL_CDECL); + assert(r >= 0); + + // UV Transform component + r = engine->RegisterGlobalFunction("void AddUVTransform(uint, float, float, float = 0.0f, float = 0.0f, float = 0.0f)", + asFUNCTION(AS_AddUVTransform), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetUVScale(uint, float, float)", + asFUNCTION(AS_SetUVScale), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetUVOffset(uint, float, float)", + asFUNCTION(AS_SetUVOffset), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetUVRotation(uint, float)", + asFUNCTION(AS_SetUVRotation), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasUVTransform(uint)", + asFUNCTION(AS_HasUVTransform), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveUVTransform(uint)", + asFUNCTION(AS_RemoveUVTransform), asCALL_CDECL); + assert(r >= 0); + + // Tag component + r = engine->RegisterGlobalFunction("void AddTag(uint, const string &in)", + asFUNCTION(AS_AddTag), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("string GetTag(uint)", + asFUNCTION(AS_GetTag), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetTag(uint, const string &in)", + asFUNCTION(AS_SetTag), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasTag(uint)", + asFUNCTION(AS_HasTag), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveTag(uint)", + asFUNCTION(AS_RemoveTag), asCALL_CDECL); + assert(r >= 0); + + // Hierarchy component + r = engine->RegisterGlobalFunction("void SetParent(uint, uint)", + asFUNCTION(AS_SetParent), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void ClearParent(uint)", + asFUNCTION(AS_ClearParent), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint GetParent(uint)", + asFUNCTION(AS_GetParent), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasParent(uint)", + asFUNCTION(AS_HasParent), asCALL_CDECL); + assert(r >= 0); + + // Camera3D component + r = engine->RegisterGlobalFunction("void AddCamera3D(uint, float, float, float, float, float, float, float)", + asFUNCTION(AS_AddCamera3D), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetCameraPosition(uint, float, float, float)", + asFUNCTION(AS_SetCameraPosition), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetCameraTarget(uint, float, float, float)", + asFUNCTION(AS_SetCameraTarget), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetCameraFovy(uint, float)", + asFUNCTION(AS_SetCameraFovy), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasCamera3D(uint)", + asFUNCTION(AS_HasCamera3D), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveCamera3D(uint)", + asFUNCTION(AS_RemoveCamera3D), asCALL_CDECL); + assert(r >= 0); + + // Light component + r = engine->RegisterGlobalFunction("void AddLight(uint, float, float, float, float = 1.0f)", + asFUNCTION(AS_AddLight), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetLightDirection(uint, float, float, float)", + asFUNCTION(AS_SetLightDirection), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetLightIntensity(uint, float)", + asFUNCTION(AS_SetLightIntensity), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetLightColor(uint, uint)", + asFUNCTION(AS_SetLightColor), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasLight(uint)", + asFUNCTION(AS_HasLight), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveLight(uint)", + asFUNCTION(AS_RemoveLight), asCALL_CDECL); + assert(r >= 0); + + // Material component + r = engine->RegisterGlobalFunction("void AddMaterial(uint, uint)", + asFUNCTION(AS_AddMaterial), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetMaterialId(uint, uint)", + asFUNCTION(AS_SetMaterialId), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint GetMaterialId(uint)", + asFUNCTION(AS_GetMaterialId), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasMaterial(uint)", + asFUNCTION(AS_HasMaterial), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveMaterial(uint)", + asFUNCTION(AS_RemoveMaterialComponent), asCALL_CDECL); + assert(r >= 0); + + // RenderPass component + r = engine->RegisterGlobalFunction("void AddRenderPass(uint, int, bool, bool, uint)", + asFUNCTION(AS_AddRenderPass), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetRenderPassOrder(uint, int)", + asFUNCTION(AS_SetRenderPassOrder), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetRenderPassShader(uint, uint)", + asFUNCTION(AS_SetRenderPassShader), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool HasRenderPass(uint)", + asFUNCTION(AS_HasRenderPass), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void RemoveRenderPass(uint)", + asFUNCTION(AS_RemoveRenderPass), asCALL_CDECL); + assert(r >= 0); + + engine->SetDefaultNamespace(""); +} diff --git a/src/scripting/ImageBindings.cpp b/src/scripting/ImageBindings.cpp new file mode 100644 index 0000000..01a8451 --- /dev/null +++ b/src/scripting/ImageBindings.cpp @@ -0,0 +1,29 @@ +#include "scripting/ImageBindings.h" +#include +#include +#include + +Image AS_LoadImage(const std::string &filename) +{ + return LoadImage(filename.c_str()); +} + +void RegisterImageBindings(asIScriptEngine *engine) +{ + int r; + + engine->SetDefaultNamespace("Texture"); + + // Register the Image type + r = engine->RegisterObjectType("Image", sizeof(Image), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits()); + assert(r >= 0); + + + // Register Image functions (example: LoadImage and UnloadImage) + r = engine->RegisterGlobalFunction("Image LoadImage(const string &in)", asFUNCTION(AS_LoadImage), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Unload(Image &in)", asFUNCTION(UnloadImage), asCALL_CDECL); + assert(r >= 0); + + engine->SetDefaultNamespace(""); +} \ No newline at end of file diff --git a/src/scripting/InputBindings.cpp b/src/scripting/InputBindings.cpp new file mode 100644 index 0000000..cb1bfe2 --- /dev/null +++ b/src/scripting/InputBindings.cpp @@ -0,0 +1,193 @@ +#include "scripting/InputBindings.h" +#include "InputManager.h" +#include +#include + +static InputManager* g_inputManager = nullptr; + +void SetGlobalInputManager(InputManager* inputManager) { + g_inputManager = inputManager; +} + +// AngelScript wrapper functions +static bool AS_IsActionPressed(const std::string& actionName) { + return g_inputManager ? g_inputManager->IsActionPressed(actionName) : false; +} + +static bool AS_IsActionDown(const std::string& actionName) { + return g_inputManager ? g_inputManager->IsActionDown(actionName) : false; +} + +static bool AS_IsActionReleased(const std::string& actionName) { + return g_inputManager ? g_inputManager->IsActionReleased(actionName) : false; +} + +static float AS_GetActionStrength(const std::string& actionName) { + return g_inputManager ? g_inputManager->GetActionStrength(actionName) : 0.0f; +} + +static void AS_RegisterAction(const std::string& actionName) { + if (g_inputManager) g_inputManager->RegisterAction(actionName); +} + +static void AS_BindKeyboard(const std::string& actionName, int keyCode) { + if (g_inputManager) g_inputManager->BindKeyboard(actionName, keyCode); +} + +static void AS_BindMouseButton(const std::string& actionName, int mouseButton) { + if (g_inputManager) g_inputManager->BindMouseButton(actionName, mouseButton); +} + +static void AS_BindGamepadButton(const std::string& actionName, int gamepadId, int button) { + if (g_inputManager) g_inputManager->BindGamepadButton(actionName, gamepadId, button); +} + +static void AS_BindGamepadAxis(const std::string& actionName, int gamepadId, int axis, float threshold, bool negative) { + if (g_inputManager) g_inputManager->BindGamepadAxis(actionName, gamepadId, axis, threshold, negative); +} + +static void AS_ClearBindings(const std::string& actionName) { + if (g_inputManager) g_inputManager->ClearBindings(actionName); +} + +static void AS_ClearAllActions() { + if (g_inputManager) g_inputManager->ClearAllActions(); +} + +void RegisterInputBindings(asIScriptEngine* engine) { + int r; + + // Input action queries + r = engine->RegisterGlobalFunction("bool IsActionPressed(const string &in)", + asFUNCTION(AS_IsActionPressed), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool IsActionDown(const string &in)", + asFUNCTION(AS_IsActionDown), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool IsActionReleased(const string &in)", + asFUNCTION(AS_IsActionReleased), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float GetActionStrength(const string &in)", + asFUNCTION(AS_GetActionStrength), asCALL_CDECL); + assert(r >= 0); + + // Allow runtime binding from scripts + r = engine->RegisterGlobalFunction("void RegisterAction(const string &in)", + asFUNCTION(AS_RegisterAction), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void BindKeyboard(const string &in, int)", + asFUNCTION(AS_BindKeyboard), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void BindMouseButton(const string &in, int)", + asFUNCTION(AS_BindMouseButton), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void BindGamepadButton(const string &in, int, int)", + asFUNCTION(AS_BindGamepadButton), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void BindGamepadAxis(const string &in, int, int, float = 0.5f, bool = false)", + asFUNCTION(AS_BindGamepadAxis), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void ClearBindings(const string &in)", + asFUNCTION(AS_ClearBindings), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void ClearAllActions()", + asFUNCTION(AS_ClearAllActions), asCALL_CDECL); + assert(r >= 0); + + // Key constants for binding + r = engine->RegisterEnum("Key"); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "W", KEY_W); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "A", KEY_A); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "S", KEY_S); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "D", KEY_D); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "SPACE", KEY_SPACE); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "E", KEY_E); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "LEFT_CONTROL", KEY_LEFT_CONTROL); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "UP", KEY_UP); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "DOWN", KEY_DOWN); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "LEFT", KEY_LEFT); + assert(r >= 0); + r = engine->RegisterEnumValue("Key", "RIGHT", KEY_RIGHT); + assert(r >= 0); + + r = engine->RegisterEnum("MouseButton"); + assert(r >= 0); + // Mouse button constants + r = engine->RegisterEnumValue("MouseButton", "LEFT", MOUSE_BUTTON_LEFT); + assert(r >= 0); + r= engine->RegisterEnumValue("MouseButton", "RIGHT", MOUSE_BUTTON_RIGHT); + assert(r >= 0); + r = engine->RegisterEnumValue("MouseButton", "MIDDLE", MOUSE_BUTTON_MIDDLE); + assert(r >= 0); + + r = engine->RegisterEnum("GamepadButton"); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "LEFT_FACE_UP", GAMEPAD_BUTTON_LEFT_FACE_UP); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "LEFT_FACE_RIGHT", GAMEPAD_BUTTON_LEFT_FACE_RIGHT); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "LEFT_FACE_DOWN", GAMEPAD_BUTTON_LEFT_FACE_DOWN); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "LEFT_FACE_LEFT", GAMEPAD_BUTTON_LEFT_FACE_LEFT); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "RIGHT_FACE_UP", GAMEPAD_BUTTON_RIGHT_FACE_UP); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "RIGHT_FACE_RIGHT", GAMEPAD_BUTTON_RIGHT_FACE_RIGHT); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "RIGHT_FACE_DOWN", GAMEPAD_BUTTON_RIGHT_FACE_DOWN); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "RIGHT_FACE_LEFT", GAMEPAD_BUTTON_RIGHT_FACE_LEFT); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "LEFT_TRIGGER_1", GAMEPAD_BUTTON_LEFT_TRIGGER_1); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "LEFT_TRIGGER_2", GAMEPAD_BUTTON_LEFT_TRIGGER_2); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "RIGHT_TRIGGER_1", GAMEPAD_BUTTON_RIGHT_TRIGGER_1); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "RIGHT_TRIGGER_2", GAMEPAD_BUTTON_RIGHT_TRIGGER_2); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "MIDDLE_LEFT", GAMEPAD_BUTTON_MIDDLE_LEFT); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "MIDDLE", GAMEPAD_BUTTON_MIDDLE); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "MIDDLE_RIGHT", GAMEPAD_BUTTON_MIDDLE_RIGHT); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "LEFT_THUMB", GAMEPAD_BUTTON_LEFT_THUMB); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadButton", "RIGHT_THUMB", GAMEPAD_BUTTON_RIGHT_THUMB); + assert(r >= 0); + + r = engine->RegisterEnum("GamepadAxis"); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadAxis", "LEFT_X", GAMEPAD_AXIS_LEFT_X); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadAxis", "LEFT_Y", GAMEPAD_AXIS_LEFT_Y); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadAxis", "RIGHT_X", GAMEPAD_AXIS_RIGHT_X); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadAxis", "RIGHT_Y", GAMEPAD_AXIS_RIGHT_Y); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadAxis", "LEFT_TRIGGER", GAMEPAD_AXIS_LEFT_TRIGGER); + assert(r >= 0); + r = engine->RegisterEnumValue("GamepadAxis", "RIGHT_TRIGGER", GAMEPAD_AXIS_RIGHT_TRIGGER); + assert(r >= 0); +} diff --git a/src/ScriptBindings.cpp b/src/scripting/LogBindings.cpp similarity index 55% rename from src/ScriptBindings.cpp rename to src/scripting/LogBindings.cpp index 5d149c0..12beb5d 100644 --- a/src/ScriptBindings.cpp +++ b/src/scripting/LogBindings.cpp @@ -1,11 +1,10 @@ -#include "ScriptBindings.h" -#include "raylib.h" -#include +#include "scripting/LogBindings.h" +#include "../log/log.h" +#include #include -#include "log/log.h" -#include "gui/Toast.h" +#include -// Expose log level constants to AngelScript +// Log level constants static int AS_LOG_TRACE = 1; static int AS_LOG_DEBUG = 2; static int AS_LOG_INFO = 3; @@ -13,75 +12,7 @@ static int AS_LOG_WARNING = 4; static int AS_LOG_ERROR = 5; static int AS_LOG_FATAL = 6; -static void AS_ToastInfo(const std::string& message) -{ - Toast::Info(message); -} - -static void AS_ToastWarning(const std::string& message) -{ - Toast::Warning(message); -} - -static void AS_ToastError(const std::string& message) -{ - Toast::Error(message); -} - -static void AS_ToastSuccess(const std::string& message) -{ - Toast::Success(message); -} -void ScriptBindings::RegisterAll(asIScriptEngine *engine) -{ - // Register Print function with generic calling convention to access context - [[maybe_unused]] int r = engine->RegisterGlobalFunction("void Print(const string &in)", - asFUNCTION(Print), asCALL_GENERIC); - assert(r >= 0); - - // Register log level constants so scripts can refer to LOG_INFO, LOG_ERROR, etc. - r = engine->RegisterGlobalProperty("int LOG_TRACE", (void *)&AS_LOG_TRACE); - assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_DEBUG", (void *)&AS_LOG_DEBUG); - assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_INFO", (void *)&AS_LOG_INFO); - assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_WARNING", (void *)&AS_LOG_WARNING); - assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_ERROR", (void *)&AS_LOG_ERROR); - assert(r >= 0); - r = engine->RegisterGlobalProperty("int LOG_FATAL", (void *)&AS_LOG_FATAL); - assert(r >= 0); - - // Register Log(level, message) to allow scripts to log at different levels - r = engine->RegisterGlobalFunction("void Log(int, const string &in)", - asFUNCTION(Log), asCALL_GENERIC); - assert(r >= 0); - - // Toast functions - engine->SetDefaultNamespace("Toast"); - r = engine->RegisterGlobalFunction("void Info(const string &in)", - asFUNCTION(AS_ToastInfo), asCALL_CDECL); - assert(r >= 0); - r = engine->RegisterGlobalFunction("void Warning(const string &in)", - asFUNCTION(AS_ToastWarning), asCALL_CDECL); - assert(r >= 0); - r = engine->RegisterGlobalFunction("void Error(const string &in)", - asFUNCTION(AS_ToastError), asCALL_CDECL); - assert(r >= 0); - r = engine->RegisterGlobalFunction("void Success(const string &in)", - asFUNCTION(AS_ToastSuccess), asCALL_CDECL); - assert(r >= 0); - engine->SetDefaultNamespace(""); - - - // Register DrawText function - r = engine->RegisterGlobalFunction("void DrawText(const string &in, int, int, int, uint)", - asFUNCTION(AS_DrawText), asCALL_CDECL); - assert(r >= 0); -} - -void ScriptBindings::Print(asIScriptGeneric *gen) +void Print(asIScriptGeneric *gen) { // Get the message parameter std::string *msg = static_cast(gen->GetArgObject(0)); @@ -115,7 +46,7 @@ void ScriptBindings::Print(asIScriptGeneric *gen) } } -void ScriptBindings::Log(asIScriptGeneric *gen) +void Log(asIScriptGeneric *gen) { // arg0: int level, arg1: string message int level = gen->GetArgDWord(0); @@ -161,17 +92,31 @@ void ScriptBindings::Log(asIScriptGeneric *gen) } } -Color ColorFromUInt(unsigned int c) +void RegisterLogBindings(asIScriptEngine *engine) { - Color col; - col.r = (c >> 24) & 0xFF; - col.g = (c >> 16) & 0xFF; - col.b = (c >> 8) & 0xFF; - col.a = c & 0xFF; - return col; -} + int r; -void ScriptBindings::AS_DrawText(const std::string &text, int x, int y, int fontSize, unsigned int color) -{ - DrawText(text.c_str(), x, y, fontSize, ColorFromUInt(color)); -} \ No newline at end of file + // Register Print function + r = engine->RegisterGlobalFunction("void Print(const string &in)", + asFUNCTION(Print), asCALL_GENERIC); + assert(r >= 0); + + // Register log level constants + r = engine->RegisterGlobalProperty("int LOG_TRACE", (void *)&AS_LOG_TRACE); + assert(r >= 0); + r = engine->RegisterGlobalProperty("int LOG_DEBUG", (void *)&AS_LOG_DEBUG); + assert(r >= 0); + r = engine->RegisterGlobalProperty("int LOG_INFO", (void *)&AS_LOG_INFO); + assert(r >= 0); + r = engine->RegisterGlobalProperty("int LOG_WARNING", (void *)&AS_LOG_WARNING); + assert(r >= 0); + r = engine->RegisterGlobalProperty("int LOG_ERROR", (void *)&AS_LOG_ERROR); + assert(r >= 0); + r = engine->RegisterGlobalProperty("int LOG_FATAL", (void *)&AS_LOG_FATAL); + assert(r >= 0); + + // Register Log function + r = engine->RegisterGlobalFunction("void Log(int, const string &in)", + asFUNCTION(Log), asCALL_GENERIC); + assert(r >= 0); +} diff --git a/src/scripting/MathBindings.cpp b/src/scripting/MathBindings.cpp new file mode 100644 index 0000000..1657399 --- /dev/null +++ b/src/scripting/MathBindings.cpp @@ -0,0 +1,52 @@ +#include "scripting/MathBindings.h" +#include +#include + +void RegisterMathBindings(asIScriptEngine *engine) +{ + int r; + + engine->SetDefaultNamespace("Math"); + + r = engine->RegisterGlobalFunction("float Cos(float)", asFUNCTION(cosf), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float Sin(float)", asFUNCTION(sinf), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float Tan(float)", asFUNCTION(tanf), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float Acos(float)", asFUNCTION(acosf), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float Asin(float)", asFUNCTION(asinf), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float Atan(float)", asFUNCTION(atanf), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float Sqrt(float)", asFUNCTION(sqrtf), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float Pow(float, float)", asFUNCTION(powf), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float Abs(float)", asFUNCTION(fabsf), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("float Log(float)", asFUNCTION(logf), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("float Log10(float)", asFUNCTION(log10f), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("float Exp(float)", asFUNCTION(expf), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("float Floor(float)", asFUNCTION(floorf), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("float Ceil(float)", asFUNCTION(ceilf), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("float Mod(float, float)", asFUNCTION(fmodf), asCALL_CDECL); + assert(r >= 0); + + engine->SetDefaultNamespace(""); +} \ No newline at end of file diff --git a/src/scripting/PhysFSBindings.cpp b/src/scripting/PhysFSBindings.cpp new file mode 100644 index 0000000..7553004 --- /dev/null +++ b/src/scripting/PhysFSBindings.cpp @@ -0,0 +1,73 @@ +#include "scripting/PhysFSBindings.h" +#include "PhysFSManager.h" +#include "scriptarray.h" +#include +#include + +static asIScriptEngine* g_engine = nullptr; + +static bool AS_Mount(const std::string& path, const std::string& mountPoint, bool append) { + return PhysFSManager::MountPath(path, mountPoint, append); +} + +static bool AS_Unmount(const std::string& path) { + return PhysFSManager::UnmountPath(path); +} + +static bool AS_Exists(const std::string& path) { + return PhysFSManager::Exists(path); +} + +static bool AS_IsDirectory(const std::string& path) { + return PhysFSManager::IsDirectory(path); +} + +static std::string AS_GetRealDir(const std::string& path) { + return PhysFSManager::GetRealDir(path); +} + +static CScriptArray* AS_ListFiles(const std::string& path) { + if (!g_engine) return nullptr; + const std::vector files = PhysFSManager::ListFiles(path); + asITypeInfo* arrayType = g_engine->GetTypeInfoByDecl("array"); + if (!arrayType) return nullptr; + + CScriptArray* array = CScriptArray::Create(arrayType, static_cast(files.size())); + for (asUINT i = 0; i < static_cast(files.size()); ++i) { + static_cast(array->At(i))->assign(files[i]); + } + return array; +} + +void RegisterPhysFSBindings(asIScriptEngine* engine) { + int r; + g_engine = engine; + + engine->SetDefaultNamespace("PhysFS"); + + r = engine->RegisterGlobalFunction("bool Mount(const string &in, const string &in mountPoint = \"\", bool append = true)", + asFUNCTION(AS_Mount), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool Unmount(const string &in)", + asFUNCTION(AS_Unmount), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool Exists(const string &in)", + asFUNCTION(AS_Exists), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool IsDirectory(const string &in)", + asFUNCTION(AS_IsDirectory), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("string GetRealDir(const string &in)", + asFUNCTION(AS_GetRealDir), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("array@ ListFiles(const string &in)", + asFUNCTION(AS_ListFiles), asCALL_CDECL); + assert(r >= 0); + + engine->SetDefaultNamespace(""); +} diff --git a/src/scripting/ResourceBindings.cpp b/src/scripting/ResourceBindings.cpp new file mode 100644 index 0000000..57f8ab9 --- /dev/null +++ b/src/scripting/ResourceBindings.cpp @@ -0,0 +1,395 @@ +#include "scripting/ResourceBindings.h" +#include "ShaderManager.h" +#include "ModelManager.h" +#include "MaterialManager.h" +#include "TextureManager.h" +#include "AssetManager.h" +#include +#include +#include "raylib.h" + +// Global resource manager pointers for script access +static ShaderManager* g_shaderManager = nullptr; +static ModelManager* g_modelManager = nullptr; +static MaterialManager* g_materialManager = nullptr; +static TextureManager* g_textureManager = nullptr; +static AssetManager* g_assetManager = nullptr; + +void SetResourceManagers(ShaderManager* shaderMgr, ModelManager* modelMgr, MaterialManager* materialMgr, TextureManager* textureMgr) { + g_shaderManager = shaderMgr; + g_modelManager = modelMgr; + g_materialManager = materialMgr; + g_textureManager = textureMgr; +} + +void SetAssetManager(AssetManager* assetMgr) { + g_assetManager = assetMgr; +} + +// === Shader Management === + +unsigned int AS_LoadShader(const std::string& vsPath, const std::string& fsPath) { + if (!g_shaderManager) return 0; + return g_shaderManager->Load(vsPath, fsPath); +} + +void AS_UnloadShader(unsigned int id) { + if (!g_shaderManager) return; + g_shaderManager->Unload(id); +} + +// === Model Management === + +unsigned int AS_LoadModel(const std::string& path) { + if (!g_modelManager) return 0; + return g_modelManager->Load(path); +} + +unsigned int AS_LoadModelCube(float width, float height, float length) { + if (!g_modelManager) return 0; + return g_modelManager->LoadCube(width, height, length); +} + +unsigned int AS_LoadModelSphere(float radius, int rings, int slices) { + if (!g_modelManager) return 0; + return g_modelManager->LoadSphere(radius, rings, slices); +} + +unsigned int AS_LoadModelPlane(float width, float length, int resX, int resZ) { + if (!g_modelManager) return 0; + return g_modelManager->LoadPlane(width, length, resX, resZ); +} + +void AS_UnloadModel(unsigned int id) { + if (!g_modelManager) return; + g_modelManager->Unload(id); +} + +// === Material Management === + +unsigned int AS_CreateMaterial(unsigned int shaderId, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + if (!g_materialManager) return 0; + Color albedo = {r, g, b, a}; + return g_materialManager->Create(shaderId, albedo); +} + +void AS_RemoveMaterial(unsigned int id) { + if (!g_materialManager) return; + g_materialManager->Remove(id); +} + +void AS_SetMaterialShader(unsigned int id, unsigned int shaderId) { + if (!g_materialManager) return; + g_materialManager->SetShader(id, shaderId); +} + +void AS_SetMaterialAlbedo(unsigned int id, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + if (!g_materialManager) return; + Color albedo = {r, g, b, a}; + g_materialManager->SetAlbedo(id, albedo); +} + +// === Texture Management === + +unsigned int AS_LoadTextureAsset(const std::string& path) { + if (!g_textureManager) return 0; + return g_textureManager->Load(path); +} + +void AS_UnloadTextureAsset(unsigned int id) { + if (!g_textureManager) return; + g_textureManager->Unload(id); +} + +// === Asset Manager === + +unsigned int AS_AssetLoadTexture(const std::string& key, const std::string& path) { + if (!g_assetManager) return 0; + return g_assetManager->LoadTexture(key, path); +} + +unsigned int AS_AssetLoadShader(const std::string& key, const std::string& vsPath, const std::string& fsPath) { + if (!g_assetManager) return 0; + return g_assetManager->LoadShader(key, vsPath, fsPath); +} + +unsigned int AS_AssetLoadModel(const std::string& key, const std::string& path) { + if (!g_assetManager) return 0; + return g_assetManager->LoadModel(key, path); +} + +unsigned int AS_AssetLoadCube(const std::string& key, float width, float height, float length) { + if (!g_assetManager) return 0; + return g_assetManager->LoadCube(key, width, height, length); +} + +unsigned int AS_AssetLoadPoly(const std::string& key, int sides, float radius) { + if (!g_assetManager) return 0; + return g_assetManager->LoadPoly(key, sides, radius); +} + +unsigned int AS_AssetLoadSphere(const std::string& key, float radius, int rings, int slices) { + if (!g_assetManager) return 0; + return g_assetManager->LoadSphere(key, radius, rings, slices); +} + +unsigned int AS_AssetLoadHemiSphere(const std::string& key, float radius, int rings, int slices) { + if (!g_assetManager) return 0; + return g_assetManager->LoadHemiSphere(key, radius, rings, slices); +} + +unsigned int AS_AssetLoadPlane(const std::string& key, float width, float length, int resX, int resZ) { + if (!g_assetManager) return 0; + return g_assetManager->LoadPlane(key, width, length, resX, resZ); +} + +unsigned int AS_AssetLoadCylinder(const std::string& key, float radius, float height, int slices) { + if (!g_assetManager) return 0; + return g_assetManager->LoadCylinder(key, radius, height, slices); +} + +unsigned int AS_AssetLoadCone(const std::string& key, float radius, float height, int slices) { + if (!g_assetManager) return 0; + return g_assetManager->LoadCone(key, radius, height, slices); +} + +unsigned int AS_AssetLoadTorus(const std::string& key, float radius, float size, int radSeg, int sides) { + if (!g_assetManager) return 0; + return g_assetManager->LoadTorus(key, radius, size, radSeg, sides); +} + +unsigned int AS_AssetLoadKnot(const std::string& key, float radius, float size, int radSeg, int sides) { + if (!g_assetManager) return 0; + return g_assetManager->LoadKnot(key, radius, size, radSeg, sides); +} + +unsigned int AS_AssetLoadHeightmap(const std::string& key, const std::string& heightmapPath, float sizeX, float sizeY, float sizeZ) { + if (!g_assetManager) return 0; + return g_assetManager->LoadHeightmap(key, heightmapPath, sizeX, sizeY, sizeZ); +} + +unsigned int AS_AssetLoadCubicmap(const std::string& key, const std::string& cubicmapPath, float sizeX, float sizeY, float sizeZ) { + if (!g_assetManager) return 0; + return g_assetManager->LoadCubicmap(key, cubicmapPath, sizeX, sizeY, sizeZ); +} + +unsigned int AS_AssetCreateMaterial(const std::string& key, unsigned int shaderId, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { + if (!g_assetManager) return 0; + Color albedo = {r, g, b, a}; + return g_assetManager->CreateMaterial(key, shaderId, albedo); +} + +bool AS_AssetUnloadTexture(const std::string& key) { + if (!g_assetManager) return false; + return g_assetManager->UnloadTexture(key); +} + +bool AS_AssetUnloadShader(const std::string& key) { + if (!g_assetManager) return false; + return g_assetManager->UnloadShader(key); +} + +bool AS_AssetUnloadModel(const std::string& key) { + if (!g_assetManager) return false; + return g_assetManager->UnloadModel(key); +} + +bool AS_AssetUnloadMaterial(const std::string& key) { + if (!g_assetManager) return false; + return g_assetManager->UnloadMaterial(key); +} + +unsigned int AS_AssetGetTextureId(const std::string& key) { + if (!g_assetManager) return 0; + return g_assetManager->GetTextureId(key); +} + +unsigned int AS_AssetGetShaderId(const std::string& key) { + if (!g_assetManager) return 0; + return g_assetManager->GetShaderId(key); +} + +unsigned int AS_AssetGetModelId(const std::string& key) { + if (!g_assetManager) return 0; + return g_assetManager->GetModelId(key); +} + +unsigned int AS_AssetGetMaterialId(const std::string& key) { + if (!g_assetManager) return 0; + return g_assetManager->GetMaterialId(key); +} + +bool AS_AssetApplyMeshTangents(const std::string& key, int meshIndex) { + if (!g_assetManager) return false; + return g_assetManager->ApplyMeshTangents(key, meshIndex); +} + +void RegisterResourceBindings(asIScriptEngine *engine) +{ + int r; + + // === Shader namespace === + engine->SetDefaultNamespace("Shader"); + + r = engine->RegisterGlobalFunction("uint Load(const string &in, const string &in)", + asFUNCTION(AS_LoadShader), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void Unload(uint)", + asFUNCTION(AS_UnloadShader), asCALL_CDECL); + assert(r >= 0); + + // === Model namespace === + engine->SetDefaultNamespace("Model"); + + r = engine->RegisterGlobalFunction("uint Load(const string &in)", + asFUNCTION(AS_LoadModel), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadCube(float, float, float)", + asFUNCTION(AS_LoadModelCube), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadSphere(float, int, int)", + asFUNCTION(AS_LoadModelSphere), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadPlane(float, float, int, int)", + asFUNCTION(AS_LoadModelPlane), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void Unload(uint)", + asFUNCTION(AS_UnloadModel), asCALL_CDECL); + assert(r >= 0); + + // === Material namespace === + engine->SetDefaultNamespace("Material"); + + r = engine->RegisterGlobalFunction("uint Create(uint, uint8, uint8, uint8, uint8)", + asFUNCTION(AS_CreateMaterial), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void Remove(uint)", + asFUNCTION(AS_RemoveMaterial), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetShader(uint, uint)", + asFUNCTION(AS_SetMaterialShader), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void SetAlbedo(uint, uint8, uint8, uint8, uint8)", + asFUNCTION(AS_SetMaterialAlbedo), asCALL_CDECL); + assert(r >= 0); + + // === TextureAsset namespace === + engine->SetDefaultNamespace("TextureAsset"); + + r = engine->RegisterGlobalFunction("uint Load(const string &in)", + asFUNCTION(AS_LoadTextureAsset), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void Unload(uint)", + asFUNCTION(AS_UnloadTextureAsset), asCALL_CDECL); + assert(r >= 0); + + // === Asset namespace === + engine->SetDefaultNamespace("Asset"); + + r = engine->RegisterGlobalFunction("uint LoadTexture(const string &in, const string &in)", + asFUNCTION(AS_AssetLoadTexture), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadShader(const string &in, const string &in, const string &in)", + asFUNCTION(AS_AssetLoadShader), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadModel(const string &in, const string &in)", + asFUNCTION(AS_AssetLoadModel), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadCube(const string &in, float, float, float)", + asFUNCTION(AS_AssetLoadCube), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadPoly(const string &in, int, float)", + asFUNCTION(AS_AssetLoadPoly), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadSphere(const string &in, float, int, int)", + asFUNCTION(AS_AssetLoadSphere), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadHemiSphere(const string &in, float, int, int)", + asFUNCTION(AS_AssetLoadHemiSphere), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadPlane(const string &in, float, float, int, int)", + asFUNCTION(AS_AssetLoadPlane), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadCylinder(const string &in, float, float, int)", + asFUNCTION(AS_AssetLoadCylinder), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadCone(const string &in, float, float, int)", + asFUNCTION(AS_AssetLoadCone), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadTorus(const string &in, float, float, int, int)", + asFUNCTION(AS_AssetLoadTorus), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadKnot(const string &in, float, float, int, int)", + asFUNCTION(AS_AssetLoadKnot), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadHeightmap(const string &in, const string &in, float, float, float)", + asFUNCTION(AS_AssetLoadHeightmap), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint LoadCubicmap(const string &in, const string &in, float, float, float)", + asFUNCTION(AS_AssetLoadCubicmap), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint CreateMaterial(const string &in, uint, uint8, uint8, uint8, uint8)", + asFUNCTION(AS_AssetCreateMaterial), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool UnloadTexture(const string &in)", + asFUNCTION(AS_AssetUnloadTexture), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool UnloadShader(const string &in)", + asFUNCTION(AS_AssetUnloadShader), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool UnloadModel(const string &in)", + asFUNCTION(AS_AssetUnloadModel), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool UnloadMaterial(const string &in)", + asFUNCTION(AS_AssetUnloadMaterial), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint GetTextureId(const string &in)", + asFUNCTION(AS_AssetGetTextureId), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint GetShaderId(const string &in)", + asFUNCTION(AS_AssetGetShaderId), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint GetModelId(const string &in)", + asFUNCTION(AS_AssetGetModelId), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("uint GetMaterialId(const string &in)", + asFUNCTION(AS_AssetGetMaterialId), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("bool ApplyMeshTangents(const string &in, int = 0)", + asFUNCTION(AS_AssetApplyMeshTangents), asCALL_CDECL); + assert(r >= 0); + + engine->SetDefaultNamespace(""); +} diff --git a/src/scripting/SceneBindings.cpp b/src/scripting/SceneBindings.cpp new file mode 100644 index 0000000..8684b0d --- /dev/null +++ b/src/scripting/SceneBindings.cpp @@ -0,0 +1,28 @@ +#include "scripting/SceneBindings.h" +#include "SceneLoader.h" +#include +#include + +static unsigned int AS_LoadScene(const std::string& path, bool clearExisting) { + entt::entity root = LoadSceneFromFile(path, clearExisting); + return static_cast(root); +} + +static void AS_ClearScene() { + ClearScene(); +} + +void RegisterSceneBindings(asIScriptEngine* engine) { + int r; + + engine->SetDefaultNamespace("Scene"); + + r = engine->RegisterGlobalFunction("uint Load(const string &in, bool clearExisting = true)", + asFUNCTION(AS_LoadScene), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void Clear()", asFUNCTION(AS_ClearScene), asCALL_CDECL); + assert(r >= 0); + + engine->SetDefaultNamespace(""); +} diff --git a/src/scripting/ScriptBindings.cpp b/src/scripting/ScriptBindings.cpp new file mode 100644 index 0000000..40a2403 --- /dev/null +++ b/src/scripting/ScriptBindings.cpp @@ -0,0 +1,26 @@ +#include "scripting/ScriptBindings.h" +#include "scripting/ToastBindings.h" +#include "scripting/LogBindings.h" +#include "scripting/ImageBindings.h" +#include "scripting/TextureBindings.h" +#include "scripting/MathBindings.h" +#include "scripting/ECSBindings.h" +#include "scripting/ResourceBindings.h" +#include "scripting/InputBindings.h" +#include "scripting/SceneBindings.h" +#include "scripting/PhysFSBindings.h" + +void ScriptBindings::RegisterAll(asIScriptEngine *engine) +{ + // Delegate to separate binding files + RegisterToastBindings(engine); + RegisterLogBindings(engine); + RegisterImageBindings(engine); + RegisterTextureBindings(engine); + RegisterMathBindings(engine); + RegisterECSBindings(engine); + RegisterResourceBindings(engine); + RegisterInputBindings(engine); + RegisterSceneBindings(engine); + RegisterPhysFSBindings(engine); +} \ No newline at end of file diff --git a/src/ScriptEngine.cpp b/src/scripting/ScriptEngine.cpp similarity index 50% rename from src/ScriptEngine.cpp rename to src/scripting/ScriptEngine.cpp index 9863604..20bf13b 100644 --- a/src/ScriptEngine.cpp +++ b/src/scripting/ScriptEngine.cpp @@ -1,17 +1,17 @@ -#include "ScriptEngine.h" -#include "ScriptBindings.h" +#include "scripting/ScriptEngine.h" +#include "scripting/ScriptBindings.h" #include "scriptstdstring.h" #include "scriptbuilder.h" -#include -#include +#include "scriptarray.h" +#include "PhysFSManager.h" #include #include -#include "log/log.h" +#include "../log/log.h" #include #include -ScriptEngine::ScriptEngine() : engine(nullptr), updateFunc(nullptr), drawFunc(nullptr), currentModule(nullptr), hasValidScript(false) { +ScriptEngine::ScriptEngine() : engine(nullptr), updateFunc(nullptr), initFunc(nullptr), shutdownFunc(nullptr),currentModule(nullptr), hasValidScript(false) { } ScriptEngine::~ScriptEngine() { @@ -28,6 +28,9 @@ bool ScriptEngine::Initialize() { // Register std::string RegisterStdString(engine); + // Register array template + RegisterScriptArray(engine, true); + // Register script bindings ScriptBindings::RegisterAll(engine); @@ -91,6 +94,9 @@ void ScriptEngine::RestoreScriptState(const std::string &state) { } void ScriptEngine::Shutdown() { + if (shutdownFunc) { + CallScriptFunction(shutdownFunc); + } ClearCachedFunctions(); if (engine) { engine->ShutDownAndRelease(); @@ -103,17 +109,23 @@ void ScriptEngine::Shutdown() { bool ScriptEngine::CompileScript(const std::string& filename) { if (!engine) return false; + std::string source; + if (!PhysFSManager::ReadTextFile(filename, source)) { + log_error("Failed to read script file: %s", filename.c_str()); + return false; + } + // Try to compile in a temporary module first using CScriptBuilder CScriptBuilder tempBuilder; tempBuilder.SetIncludeCallback(IncludeCallback, nullptr); - + int r = tempBuilder.StartNewModule(engine, "temp"); if (r < 0) { log_error("Failed to start new temp module"); return false; } - r = tempBuilder.AddSectionFromFile(filename.c_str()); + r = tempBuilder.AddSectionFromMemory(filename.c_str(), source.c_str(), static_cast(source.size())); if (r < 0) { log_error("Failed to add script section from file: %s", filename.c_str()); engine->DiscardModule("temp"); @@ -132,22 +144,22 @@ bool ScriptEngine::CompileScript(const std::string& filename) { if (currentModule) { engine->DiscardModule("main"); } - + // Create new main module with the working code using CScriptBuilder CScriptBuilder mainBuilder; mainBuilder.SetIncludeCallback(IncludeCallback, nullptr); - + r = mainBuilder.StartNewModule(engine, "main"); if (r >= 0) { - r = mainBuilder.AddSectionFromFile(filename.c_str()); + r = mainBuilder.AddSectionFromMemory(filename.c_str(), source.c_str(), static_cast(source.size())); } if (r >= 0) { r = mainBuilder.BuildModule(); } - + // Clean up temp module engine->DiscardModule("temp"); - + if (r < 0) { // This shouldn't happen since we already tested compilation log_error("Unexpected error when creating main module"); @@ -157,15 +169,69 @@ bool ScriptEngine::CompileScript(const std::string& filename) { // Get the newly created module currentModule = engine->GetModule("main"); - - // Cache new functions - updateFunc = currentModule->GetFunctionByName("Update"); - drawFunc = currentModule->GetFunctionByName("Draw"); + + // Cache new functions (scene-specific names preferred) + initFunc = currentModule->GetFunctionByDecl("void SceneInit()"); + if (!initFunc) { + initFunc = currentModule->GetFunctionByDecl("void Init()"); + } + + updateFunc = currentModule->GetFunctionByDecl("void SceneUpdate(float)"); + if (!updateFunc) { + updateFunc = currentModule->GetFunctionByDecl("void Update(float)"); + } + + shutdownFunc = currentModule->GetFunctionByDecl("void SceneShutdown()"); + if (!shutdownFunc) { + shutdownFunc = currentModule->GetFunctionByDecl("void Shutdown()"); + } hasValidScript = true; log_info("Script compiled and cached: %s", filename.c_str()); return true; } +bool ScriptEngine::CompileModule(const std::string& moduleName, const std::string& filename, asIScriptModule** outModule) { + if (!engine) return false; + + std::string source; + if (!PhysFSManager::ReadTextFile(filename, source)) { + log_error("Failed to read script module file: %s", filename.c_str()); + return false; + } + + if (engine->GetModule(moduleName.c_str())) { + engine->DiscardModule(moduleName.c_str()); + } + + CScriptBuilder builder; + builder.SetIncludeCallback(IncludeCallback, nullptr); + + int r = builder.StartNewModule(engine, moduleName.c_str()); + if (r < 0) { + log_error("Failed to start module: %s", moduleName.c_str()); + return false; + } + + r = builder.AddSectionFromMemory(filename.c_str(), source.c_str(), static_cast(source.size())); + if (r < 0) { + log_error("Failed to add script section from file: %s", filename.c_str()); + engine->DiscardModule(moduleName.c_str()); + return false; + } + + r = builder.BuildModule(); + if (r < 0) { + log_error("Failed to build module: %s", moduleName.c_str()); + engine->DiscardModule(moduleName.c_str()); + return false; + } + + if (outModule) { + *outModule = engine->GetModule(moduleName.c_str()); + } + return true; +} + void ScriptEngine::CallScriptFunction(asIScriptFunction* func, float dt) { if (!func || !engine || !hasValidScript) return; @@ -192,9 +258,162 @@ void ScriptEngine::CallScriptFunction(asIScriptFunction* func, float dt) { ctx->Release(); } +bool ScriptEngine::CallModuleFunction(asIScriptModule* module, const std::string& decl) { + if (!engine || !module) return false; + + asIScriptFunction* func = module->GetFunctionByDecl(decl.c_str()); + if (!func) return false; + + asIScriptContext* ctx = engine->CreateContext(); + if (!ctx) return false; + + int r = ctx->Prepare(func); + if (r < 0) { + ctx->Release(); + return false; + } + + r = ctx->Execute(); + if (r != asEXECUTION_FINISHED) { + if (r == asEXECUTION_EXCEPTION) { + log_error("Script exception: %s", ctx->GetExceptionString()); + } + ctx->Release(); + return false; + } + + ctx->Release(); + return true; +} + +void ScriptEngine::CallScriptFunction(asIScriptFunction* func, unsigned int entityId, float dt) { + if (!func || !engine) return; + + asIScriptContext* ctx = engine->CreateContext(); + if (!ctx) return; + + int r = ctx->Prepare(func); + if (r < 0) { + ctx->Release(); + return; + } + + const asUINT paramCount = func->GetParamCount(); + if (paramCount >= 1) { + ctx->SetArgDWord(0, entityId); + } + if (paramCount >= 2) { + ctx->SetArgFloat(1, dt); + } + + r = ctx->Execute(); + if (r != asEXECUTION_FINISHED) { + if (r == asEXECUTION_EXCEPTION) { + log_error("Script exception: %s", ctx->GetExceptionString()); + } + } + + ctx->Release(); +} + +asIScriptObject* ScriptEngine::CreateObjectInstance(asIScriptModule* module, const std::string& className) +{ + if (!engine || !module) return nullptr; + asITypeInfo* typeInfo = nullptr; + int count = module->GetObjectTypeCount(); + for (int i = 0; i < count; ++i) { + asITypeInfo* t = module->GetObjectTypeByIndex(i); + const char* n = t->GetName(); + if (n && className == n) { + typeInfo = t; + break; + } + } + if (!typeInfo) return nullptr; + void* raw = engine->CreateScriptObject(typeInfo); + return reinterpret_cast(raw); +} + +void ScriptEngine::ReleaseObjectInstance(asIScriptObject* obj) +{ + if (!obj) return; + // Prefer using the engine helper to properly destroy the script object + asITypeInfo* ti = obj->GetObjectType(); + if (engine && ti) { + engine->ReleaseScriptObject(obj, ti); + } else { + obj->Release(); + } +} + +asIScriptFunction* ScriptEngine::GetObjectMethod(asIScriptModule* module, const std::string& className, const std::string& methodDecl) +{ + if (!module) return nullptr; + asITypeInfo* typeInfo = module->GetTypeInfoByName(className.c_str()); + if (!typeInfo) return nullptr; + return typeInfo->GetMethodByDecl(methodDecl.c_str()); +} + +void ScriptEngine::CallObjectMethod(asIScriptFunction* method, asIScriptObject* object, unsigned int entityId, float dt) +{ + if (!method || !engine || !object) return; + + asIScriptContext* ctx = engine->CreateContext(); + if (!ctx) return; + + int r = ctx->Prepare(method); + if (r < 0) { ctx->Release(); return; } + + // Set the object instance for the call + ctx->SetObject(object); + + const asUINT paramCount = method->GetParamCount(); + if (paramCount >= 1) { + ctx->SetArgDWord(0, entityId); + } + if (paramCount >= 2) { + ctx->SetArgFloat(1, dt); + } + + r = ctx->Execute(); + if (r != asEXECUTION_FINISHED) { + if (r == asEXECUTION_EXCEPTION) { + log_error("Script exception: %s", ctx->GetExceptionString()); + } + } + + ctx->Release(); +} + +void ScriptEngine::CallSceneObjectMethod(asIScriptFunction* method, asIScriptObject* object, float dt) +{ + if (!method || !engine || !object) return; + + asIScriptContext* ctx = engine->CreateContext(); + if (!ctx) return; + + int r = ctx->Prepare(method); + if (r < 0) { ctx->Release(); return; } + + ctx->SetObject(object); + + const asUINT paramCount = method->GetParamCount(); + if (paramCount >= 1) { + ctx->SetArgFloat(0, dt); + } + + r = ctx->Execute(); + if (r != asEXECUTION_FINISHED) { + if (r == asEXECUTION_EXCEPTION) { + log_error("Script exception: %s", ctx->GetExceptionString()); + } + } + + ctx->Release(); +} + void ScriptEngine::ClearCachedFunctions() { updateFunc = nullptr; - drawFunc = nullptr; hasValidScript = false; } @@ -226,10 +445,10 @@ int ScriptEngine::IncludeCallback(const char* include, const char* from, CScript fromFile = lastSep + 1; } } - + // Log the include request with clean filenames log_info("Including: %s (from: %s)", include, fromFile ? fromFile : "main"); - + // Build the full path candidates: relative to 'from' and any include paths std::vector candidates; @@ -251,7 +470,11 @@ int ScriptEngine::IncludeCallback(const char* include, const char* from, CScript int result = -1; for (auto &cand : candidates) { - result = builder->AddSectionFromFile(cand.c_str()); + std::string source; + if (!PhysFSManager::ReadTextFile(cand, source)) { + continue; + } + result = builder->AddSectionFromMemory(cand.c_str(), source.c_str(), static_cast(source.size())); if (result >= 0) { log_info("Successfully included: %s", cand.c_str()); return result; @@ -263,9 +486,9 @@ int ScriptEngine::IncludeCallback(const char* include, const char* from, CScript } std::string ScriptEngine::ReadFile(const std::string& filename) { - std::ifstream file(filename); - if (!file) return ""; - std::stringstream ss; - ss << file.rdbuf(); - return ss.str(); -} \ No newline at end of file + std::string out; + if (!PhysFSManager::ReadTextFile(filename, out)) { + return std::string(); + } + return out; +} diff --git a/src/scripting/TextureBindings.cpp b/src/scripting/TextureBindings.cpp new file mode 100644 index 0000000..b4905ff --- /dev/null +++ b/src/scripting/TextureBindings.cpp @@ -0,0 +1,48 @@ +#include "scripting/TextureBindings.h" +#include +#include +#include + +// Utility function to convert uint color to Raylib Color +Color ColorFromUInt(unsigned int c) +{ + Color col; + col.r = (c >> 24) & 0xFF; + col.g = (c >> 16) & 0xFF; + col.b = (c >> 8) & 0xFF; + col.a = c & 0xFF; + return col; +} + +Texture2D AS_LoadTexture(const std::string &filename) +{ + return LoadTexture(filename.c_str()); +} + +void AS_DrawTexture(const Texture2D &texture, float x, float y, int tint) +{ + DrawTexture(texture, static_cast(x), static_cast(y), ColorFromUInt(tint)); +} + +void RegisterTextureBindings(asIScriptEngine *engine) +{ + int r; + + engine->SetDefaultNamespace("Texture"); + + // Register the Texture2D type + r = engine->RegisterObjectType("Texture2D", sizeof(Texture2D), asOBJ_VALUE | asOBJ_POD | asGetTypeTraits()); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("Texture2D LoadTexture(const string &in)", asFUNCTION(AS_LoadTexture), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void Unload(Texture2D &in)", asFUNCTION(UnloadTexture), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("Texture2D LoadFromImage(const Image &in)", asFUNCTION(LoadTextureFromImage), asCALL_CDECL); + assert(r >= 0); + + r = engine->RegisterGlobalFunction("void Draw(const Texture2D &in, float x, float y, int tint)", asFUNCTION(AS_DrawTexture), asCALL_CDECL); + assert(r >= 0); +} diff --git a/src/scripting/ToastBindings.cpp b/src/scripting/ToastBindings.cpp new file mode 100644 index 0000000..7494e73 --- /dev/null +++ b/src/scripting/ToastBindings.cpp @@ -0,0 +1,50 @@ +#include "scripting/ToastBindings.h" +#include "gui/Toast.h" +#include +#include +#include +#include "../log/log.h" +// Toast binding functions +static void AS_ToastInfo(const std::string& message) +{ + log_info("%s", message.c_str()); + Toast::Info(message); +} + +static void AS_ToastWarning(const std::string& message) +{ + log_warn("%s", message.c_str()); + Toast::Warning(message); +} + +static void AS_ToastError(const std::string& message) +{ + log_error("%s", message.c_str()); + Toast::Error(message); +} + +static void AS_ToastSuccess(const std::string& message) +{ + log_info("%s", message.c_str()); + Toast::Success(message); +} + +void RegisterToastBindings(asIScriptEngine *engine) +{ + int r; + + engine->SetDefaultNamespace("Toast"); + r = engine->RegisterGlobalFunction("void Info(const string &in)", + asFUNCTION(AS_ToastInfo), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Warning(const string &in)", + asFUNCTION(AS_ToastWarning), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Error(const string &in)", + asFUNCTION(AS_ToastError), asCALL_CDECL); + assert(r >= 0); + r = engine->RegisterGlobalFunction("void Success(const string &in)", + asFUNCTION(AS_ToastSuccess), asCALL_CDECL); + assert(r >= 0); + engine->SetDefaultNamespace(""); +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 3dd2033..c4c46ec 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,34 +1,44 @@ cmake_minimum_required(VERSION 3.16) -# Simple unit test executable that compiles acutest.c directly +# Unit tests just need test sources + link to simian_core add_executable(unit_tests - unit_tests.cpp - ${CMAKE_SOURCE_DIR}/src/HotReload.cpp - ${CMAKE_SOURCE_DIR}/src/ScriptEngine.cpp - ${CMAKE_SOURCE_DIR}/src/ScriptBindings.cpp - ${CMAKE_SOURCE_DIR}/src/log/log.c + test.cpp + physfs_test_utils.cpp + hot_reload.cpp + script_engine_tests.cpp + math_bindings_tests.cpp + resource_tests.cpp + scenegraph_tests.cpp ) target_include_directories(unit_tests PRIVATE - ${CMAKE_SOURCE_DIR}/external/acutest/include + ${CMAKE_SOURCE_DIR}/tests ${CMAKE_SOURCE_DIR}/include - ${CMAKE_SOURCE_DIR}/src/log - ${CMAKE_SOURCE_DIR}/external/angelscript/sdk/angelscript/include - ${CMAKE_SOURCE_DIR}/external/angelscript/sdk/add_on/scriptstdstring - ${CMAKE_SOURCE_DIR}/external/angelscript/sdk/add_on/scriptbuilder - ${CMAKE_SOURCE_DIR}/external/raylib/src - ${CMAKE_SOURCE_DIR}/external/imgui - ${CMAKE_SOURCE_DIR}/external/rlImGui - + ${CMAKE_SOURCE_DIR}/external/acutest ) -# Link AngelScript and its add-ons needed by ScriptEngine -target_link_libraries(unit_tests PRIVATE - angelscript - scriptstdstring - scriptbuilder - raylib - imgui -) +# Link the shared simian_core library - that's it! +target_link_libraries(unit_tests PRIVATE simian_core) -add_test(NAME unit_tests COMMAND unit_tests) \ No newline at end of file +# Platform-specific linking for tests too +if(WIN32) + target_link_libraries(unit_tests PRIVATE winmm gdi32) +endif() + +add_test(NAME unit_tests COMMAND unit_tests) +add_test(NAME hotreload_dir COMMAND unit_tests hotreload_dir) +add_test(NAME script_compile COMMAND unit_tests script_compile) +add_test(NAME script_include COMMAND unit_tests script_include) +add_test(NAME math_bindings COMMAND unit_tests math_bindings) +add_test(NAME shader_manager COMMAND unit_tests shader_manager) +add_test(NAME model_manager COMMAND unit_tests model_manager) +add_test(NAME material_manager COMMAND unit_tests material_manager) +add_test(NAME asset_registry_material COMMAND unit_tests asset_registry_material) +add_test(NAME resource_bindings COMMAND unit_tests resource_bindings) +add_test(NAME scenegraph_world COMMAND unit_tests scenegraph_world) +add_test(NAME scenegraph_detach COMMAND unit_tests scenegraph_detach) +add_test(NAME scene_loader COMMAND unit_tests scene_loader) +add_test(NAME scene_loader_script_path COMMAND unit_tests scene_loader_script_path) +add_test(NAME scene_loader_clear_script COMMAND unit_tests scene_loader_clear_script) +add_test(NAME scene_loader_assets COMMAND unit_tests scene_loader_assets) +add_test(NAME scene_loader_sandbox COMMAND unit_tests scene_loader_sandbox) diff --git a/tests/acutest.h b/tests/acutest.h new file mode 100644 index 0000000..ce5956e --- /dev/null +++ b/tests/acutest.h @@ -0,0 +1,1994 @@ +/* + * Acutest -- Another C/C++ Unit Test facility + * + * + * Copyright 2013-2023 Martin Mitáš + * Copyright 2019 Garrett D'Amore + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#ifndef ACUTEST_H +#define ACUTEST_H + + +/* Try to auto-detect whether we need to disable C++ exception handling. + * If the detection fails, you may always define TEST_NO_EXCEPTIONS before + * including "acutest.h" manually. */ +#ifdef __cplusplus + #if (__cplusplus >= 199711L && !defined __cpp_exceptions) || \ + ((defined(__GNUC__) || defined(__clang__)) && !defined __EXCEPTIONS) + #ifndef TEST_NO_EXCEPTIONS + #define TEST_NO_EXCEPTIONS + #endif + #endif +#endif + + +/************************ + *** Public interface *** + ************************/ + +/* By default, "acutest.h" provides the main program entry point (function + * main()). However, if the test suite is composed of multiple source files + * which include "acutest.h", then this causes a problem of multiple main() + * definitions. To avoid this problem, #define macro TEST_NO_MAIN in all + * compilation units but one. + */ + +/* Macro to specify list of unit tests in the suite. + * The unit test implementation MUST provide list of unit tests it implements + * with this macro: + * + * TEST_LIST = { + * { "test1_name", test1_func_ptr }, + * { "test2_name", test2_func_ptr }, + * ... + * { NULL, NULL } // zeroed record marking the end of the list + * }; + * + * The list specifies names of each test (must be unique) and pointer to + * a function implementing it. The function does not take any arguments + * and has no return values, i.e. every test function has to be compatible + * with this prototype: + * + * void test_func(void); + * + * Note the list has to be ended with a zeroed record. + */ +#define TEST_LIST const struct acutest_test_ acutest_list_[] + + +/* Macros for testing whether an unit test succeeds or fails. These macros + * can be used arbitrarily in functions implementing the unit tests. + * + * If any condition fails throughout execution of a test, the test fails. + * + * TEST_CHECK takes only one argument (the condition), TEST_CHECK_ allows + * also to specify an error message to print out if the condition fails. + * (It expects printf-like format string and its parameters). The macros + * return non-zero (condition passes) or 0 (condition fails). + * + * That can be useful when more conditions should be checked only if some + * preceding condition passes, as illustrated in this code snippet: + * + * SomeStruct* ptr = allocate_some_struct(); + * if(TEST_CHECK(ptr != NULL)) { + * TEST_CHECK(ptr->member1 < 100); + * TEST_CHECK(ptr->member2 > 200); + * } + */ +#define TEST_CHECK_(cond,...) \ + acutest_check_(!!(cond), __FILE__, __LINE__, __VA_ARGS__) +#define TEST_CHECK(cond) \ + acutest_check_(!!(cond), __FILE__, __LINE__, "%s", #cond) + + +/* These macros are the same as TEST_CHECK_ and TEST_CHECK except that if the + * condition fails, the currently executed unit test is immediately aborted. + * + * That is done either by calling abort() if the unit test is executed as a + * child process; or via longjmp() if the unit test is executed within the + * main Acutest process. + * + * As a side effect of such abortion, your unit tests may cause memory leaks, + * unflushed file descriptors, and other phenomena caused by the abortion. + * + * Therefore you should not use these as a general replacement for TEST_CHECK. + * Use it with some caution, especially if your test causes some other side + * effects to the outside world (e.g. communicating with some server, inserting + * into a database etc.). + */ +#define TEST_ASSERT_(cond,...) \ + do { \ + if(!acutest_check_(!!(cond), __FILE__, __LINE__, __VA_ARGS__)) \ + acutest_abort_(); \ + } while(0) +#define TEST_ASSERT(cond) \ + do { \ + if(!acutest_check_(!!(cond), __FILE__, __LINE__, "%s", #cond)) \ + acutest_abort_(); \ + } while(0) + + +#ifdef __cplusplus +#ifndef TEST_NO_EXCEPTIONS +/* Macros to verify that the code (the 1st argument) throws exception of given + * type (the 2nd argument). (Note these macros are only available in C++.) + * + * TEST_EXCEPTION_ is like TEST_EXCEPTION but accepts custom printf-like + * message. + * + * For example: + * + * TEST_EXCEPTION(function_that_throw(), ExpectedExceptionType); + * + * If the function_that_throw() throws ExpectedExceptionType, the check passes. + * If the function throws anything incompatible with ExpectedExceptionType + * (or if it does not thrown an exception at all), the check fails. + */ +#define TEST_EXCEPTION(code, exctype) \ + do { \ + bool exc_ok_ = false; \ + const char *msg_ = NULL; \ + try { \ + code; \ + msg_ = "No exception thrown."; \ + } catch(exctype const&) { \ + exc_ok_= true; \ + } catch(...) { \ + msg_ = "Unexpected exception thrown."; \ + } \ + acutest_check_(exc_ok_, __FILE__, __LINE__, #code " throws " #exctype);\ + if(msg_ != NULL) \ + acutest_message_("%s", msg_); \ + } while(0) +#define TEST_EXCEPTION_(code, exctype, ...) \ + do { \ + bool exc_ok_ = false; \ + const char *msg_ = NULL; \ + try { \ + code; \ + msg_ = "No exception thrown."; \ + } catch(exctype const&) { \ + exc_ok_= true; \ + } catch(...) { \ + msg_ = "Unexpected exception thrown."; \ + } \ + acutest_check_(exc_ok_, __FILE__, __LINE__, __VA_ARGS__); \ + if(msg_ != NULL) \ + acutest_message_("%s", msg_); \ + } while(0) +#endif /* #ifndef TEST_NO_EXCEPTIONS */ +#endif /* #ifdef __cplusplus */ + + +/* Sometimes it is useful to split execution of more complex unit tests to some + * smaller parts and associate those parts with some names. + * + * This is especially handy if the given unit test is implemented as a loop + * over some vector of multiple testing inputs. Using these macros allow to use + * sort of subtitle for each iteration of the loop (e.g. outputting the input + * itself or a name associated to it), so that if any TEST_CHECK condition + * fails in the loop, it can be easily seen which iteration triggers the + * failure, without the need to manually output the iteration-specific data in + * every single TEST_CHECK inside the loop body. + * + * TEST_CASE allows to specify only single string as the name of the case, + * TEST_CASE_ provides all the power of printf-like string formatting. + * + * Note that the test cases cannot be nested. Starting a new test case ends + * implicitly the previous one. To end the test case explicitly (e.g. to end + * the last test case after exiting the loop), you may use TEST_CASE(NULL). + */ +#define TEST_CASE_(...) acutest_case_(__VA_ARGS__) +#define TEST_CASE(name) acutest_case_("%s", name) + + +/* Maximal output per TEST_CASE call. Longer messages are cut. + * You may define another limit prior including "acutest.h" + */ +#ifndef TEST_CASE_MAXSIZE + #define TEST_CASE_MAXSIZE 64 +#endif + + +/* printf-like macro for outputting an extra information about a failure. + * + * Intended use is to output some computed output versus the expected value, + * e.g. like this: + * + * if(!TEST_CHECK(produced == expected)) { + * TEST_MSG("Expected: %d", expected); + * TEST_MSG("Produced: %d", produced); + * } + * + * Note the message is only written down if the most recent use of any checking + * macro (like e.g. TEST_CHECK or TEST_EXCEPTION) in the current test failed. + * This means the above is equivalent to just this: + * + * TEST_CHECK(produced == expected); + * TEST_MSG("Expected: %d", expected); + * TEST_MSG("Produced: %d", produced); + * + * The macro can deal with multi-line output fairly well. It also automatically + * adds a final new-line if there is none present. + */ +#define TEST_MSG(...) acutest_message_(__VA_ARGS__) + + +/* Maximal output per TEST_MSG call. Longer messages are cut. + * You may define another limit prior including "acutest.h" + */ +#ifndef TEST_MSG_MAXSIZE + #define TEST_MSG_MAXSIZE 1024 +#endif + + +/* Macro for dumping a block of memory. + * + * Its intended use is very similar to what TEST_MSG is for, but instead of + * generating any printf-like message, this is for dumping raw block of a + * memory in a hexadecimal form: + * + * TEST_CHECK(size_produced == size_expected && + * memcmp(addr_produced, addr_expected, size_produced) == 0); + * TEST_DUMP("Expected:", addr_expected, size_expected); + * TEST_DUMP("Produced:", addr_produced, size_produced); + */ +#define TEST_DUMP(title, addr, size) acutest_dump_(title, addr, size) + +/* Maximal output per TEST_DUMP call (in bytes to dump). Longer blocks are cut. + * You may define another limit prior including "acutest.h" + */ +#ifndef TEST_DUMP_MAXSIZE + #define TEST_DUMP_MAXSIZE 1024 +#endif + + +/* Macros for marking the test as SKIPPED. + * Note it can only be used at the beginning of a test, before any other + * checking. + * + * Once used, the best practice is to return from the test routine as soon + * as possible. + */ +#define TEST_SKIP(...) acutest_skip_(__FILE__, __LINE__, __VA_ARGS__) + + +/* Common test initialisation/clean-up + * + * In some test suites, it may be needed to perform some sort of the same + * initialization and/or clean-up in all the tests. + * + * Such test suites may use macros TEST_INIT and/or TEST_FINI prior including + * this header. The expansion of the macro is then used as a body of helper + * function called just before executing every single (TEST_INIT) or just after + * it ends (TEST_FINI). + * + * Examples of various ways how to use the macro TEST_INIT: + * + * #define TEST_INIT my_init_func(); + * #define TEST_INIT my_init_func() // Works even without the semicolon + * #define TEST_INIT setlocale(LC_ALL, NULL); + * #define TEST_INIT { setlocale(LC_ALL, NULL); my_init_func(); } + * + * TEST_FINI is to be used in the same way. + */ + + +/********************** + *** Implementation *** + **********************/ + +/* The unit test files should not rely on anything below. */ + +#include + +/* Enable the use of the non-standard keyword __attribute__ to silence warnings under some compilers */ +#if defined(__GNUC__) || defined(__clang__) + #define ACUTEST_ATTRIBUTE_(attr) __attribute__((attr)) +#else + #define ACUTEST_ATTRIBUTE_(attr) +#endif + +#ifdef __cplusplus + extern "C" { +#endif + +enum acutest_state_ { + ACUTEST_STATE_INITIAL = -4, + ACUTEST_STATE_SELECTED = -3, + ACUTEST_STATE_NEEDTORUN = -2, + + /* By the end all tests should be in one of the following: */ + ACUTEST_STATE_EXCLUDED = -1, + ACUTEST_STATE_SUCCESS = 0, + ACUTEST_STATE_FAILED = 1, + ACUTEST_STATE_SKIPPED = 2 +}; + +int acutest_check_(int cond, const char* file, int line, const char* fmt, ...); +void acutest_case_(const char* fmt, ...); +void acutest_message_(const char* fmt, ...); +void acutest_dump_(const char* title, const void* addr, size_t size); +void acutest_abort_(void) ACUTEST_ATTRIBUTE_(noreturn); +#ifdef __cplusplus + } /* extern "C" */ +#endif + +#ifndef TEST_NO_MAIN + +#include +#include +#include +#include +#include + +#if defined(unix) || defined(__unix__) || defined(__unix) || defined(__APPLE__) + #define ACUTEST_UNIX_ 1 + #include + #include + #include + #include + #include + #include + #include + + #if defined CLOCK_PROCESS_CPUTIME_ID && defined CLOCK_MONOTONIC + #define ACUTEST_HAS_POSIX_TIMER_ 1 + #endif +#endif + +#if defined(_gnu_linux_) || defined(__linux__) + #define ACUTEST_LINUX_ 1 + #include + #include +#endif + +#if defined(_WIN32) || defined(__WIN32__) || defined(__WINDOWS__) + #define ACUTEST_WIN_ 1 + #include + #include +#endif + +#if defined(__APPLE__) + #define ACUTEST_MACOS_ + #include + #include + #include + #include + #include +#endif + +#ifdef __cplusplus +#ifndef TEST_NO_EXCEPTIONS + #include +#endif +#endif + +#ifdef __has_include + #if __has_include() + #include + #endif +#endif + +/* Note our global private identifiers end with '_' to mitigate risk of clash + * with the unit tests implementation. */ + +#ifdef __cplusplus + extern "C" { +#endif + +#ifdef _MSC_VER + /* In the multi-platform code like ours, we cannot use the non-standard + * "safe" functions from Microsoft C lib like e.g. sprintf_s() instead of + * standard sprintf(). Hence, lets disable the warning C4996. */ + #pragma warning(push) + #pragma warning(disable: 4996) +#endif + + +struct acutest_test_ { + const char* name; + void (*func)(void); +}; + +struct acutest_test_data_ { + enum acutest_state_ state; + double duration; +}; + + +extern const struct acutest_test_ acutest_list_[]; + + +static char* acutest_argv0_ = NULL; +static int acutest_list_size_ = 0; +static struct acutest_test_data_* acutest_test_data_ = NULL; +static int acutest_no_exec_ = -1; +static int acutest_no_summary_ = 0; +static int acutest_tap_ = 0; +static int acutest_exclude_mode_ = 0; +static int acutest_worker_ = 0; +static int acutest_worker_index_ = 0; +static int acutest_cond_failed_ = 0; +static FILE *acutest_xml_output_ = NULL; + +static const struct acutest_test_* acutest_current_test_ = NULL; +static int acutest_current_index_ = 0; +static char acutest_case_name_[TEST_CASE_MAXSIZE] = ""; +static int acutest_test_check_count_ = 0; +static int acutest_test_skip_count_ = 0; +static char acutest_test_skip_reason_[256] = ""; +static int acutest_test_already_logged_ = 0; +static int acutest_case_already_logged_ = 0; +static int acutest_verbose_level_ = 2; +static int acutest_test_failures_ = 0; +static int acutest_colorize_ = 0; +static int acutest_timer_ = 0; + +static int acutest_abort_has_jmp_buf_ = 0; +static jmp_buf acutest_abort_jmp_buf_; + +static int +acutest_count_(enum acutest_state_ state) +{ + int i, n; + + for(i = 0, n = 0; i < acutest_list_size_; i++) { + if(acutest_test_data_[i].state == state) + n++; + } + + return n; +} + +static void +acutest_cleanup_(void) +{ + free((void*) acutest_test_data_); +} + +static void ACUTEST_ATTRIBUTE_(noreturn) +acutest_exit_(int exit_code) +{ + acutest_cleanup_(); + exit(exit_code); +} + + +#if defined ACUTEST_WIN_ + typedef LARGE_INTEGER acutest_timer_type_; + static LARGE_INTEGER acutest_timer_freq_; + static acutest_timer_type_ acutest_timer_start_; + static acutest_timer_type_ acutest_timer_end_; + + static void + acutest_timer_init_(void) + { + QueryPerformanceFrequency(´st_timer_freq_); + } + + static void + acutest_timer_get_time_(LARGE_INTEGER* ts) + { + QueryPerformanceCounter(ts); + } + + static double + acutest_timer_diff_(LARGE_INTEGER start, LARGE_INTEGER end) + { + double duration = (double)(end.QuadPart - start.QuadPart); + duration /= (double)acutest_timer_freq_.QuadPart; + return duration; + } + + static void + acutest_timer_print_diff_(void) + { + printf("%.6lf secs", acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_)); + } +#elif defined ACUTEST_HAS_POSIX_TIMER_ + static clockid_t acutest_timer_id_; + typedef struct timespec acutest_timer_type_; + static acutest_timer_type_ acutest_timer_start_; + static acutest_timer_type_ acutest_timer_end_; + + static void + acutest_timer_init_(void) + { + if(acutest_timer_ == 1) + acutest_timer_id_ = CLOCK_MONOTONIC; + else if(acutest_timer_ == 2) + acutest_timer_id_ = CLOCK_PROCESS_CPUTIME_ID; + } + + static void + acutest_timer_get_time_(struct timespec* ts) + { + clock_gettime(acutest_timer_id_, ts); + } + + static double + acutest_timer_diff_(struct timespec start, struct timespec end) + { + return (double)(end.tv_sec - start.tv_sec) + (double)(end.tv_nsec - start.tv_nsec) / 1e9; + } + + static void + acutest_timer_print_diff_(void) + { + printf("%.6lf secs", + acutest_timer_diff_(acutest_timer_start_, acutest_timer_end_)); + } +#else + typedef int acutest_timer_type_; + static acutest_timer_type_ acutest_timer_start_; + static acutest_timer_type_ acutest_timer_end_; + + void + acutest_timer_init_(void) + {} + + static void + acutest_timer_get_time_(int* ts) + { + (void) ts; + } + + static double + acutest_timer_diff_(int start, int end) + { + (void) start; + (void) end; + return 0.0; + } + + static void + acutest_timer_print_diff_(void) + {} +#endif + +#define ACUTEST_COLOR_DEFAULT_ 0 +#define ACUTEST_COLOR_RED_ 1 +#define ACUTEST_COLOR_GREEN_ 2 +#define ACUTEST_COLOR_YELLOW_ 3 +#define ACUTEST_COLOR_DEFAULT_INTENSIVE_ 10 +#define ACUTEST_COLOR_RED_INTENSIVE_ 11 +#define ACUTEST_COLOR_GREEN_INTENSIVE_ 12 +#define ACUTEST_COLOR_YELLOW_INTENSIVE_ 13 + +static int ACUTEST_ATTRIBUTE_(format (printf, 2, 3)) +acutest_colored_printf_(int color, const char* fmt, ...) +{ + va_list args; + char buffer[256]; + int n; + + va_start(args, fmt); + vsnprintf(buffer, sizeof(buffer), fmt, args); + va_end(args); + buffer[sizeof(buffer)-1] = '\0'; + + if(!acutest_colorize_) { + return printf("%s", buffer); + } + +#if defined ACUTEST_UNIX_ + { + const char* col_str; + switch(color) { + case ACUTEST_COLOR_RED_: col_str = "\033[0;31m"; break; + case ACUTEST_COLOR_GREEN_: col_str = "\033[0;32m"; break; + case ACUTEST_COLOR_YELLOW_: col_str = "\033[0;33m"; break; + case ACUTEST_COLOR_RED_INTENSIVE_: col_str = "\033[1;31m"; break; + case ACUTEST_COLOR_GREEN_INTENSIVE_: col_str = "\033[1;32m"; break; + case ACUTEST_COLOR_YELLOW_INTENSIVE_: col_str = "\033[1;33m"; break; + case ACUTEST_COLOR_DEFAULT_INTENSIVE_: col_str = "\033[1m"; break; + default: col_str = "\033[0m"; break; + } + printf("%s", col_str); + n = printf("%s", buffer); + printf("\033[0m"); + return n; + } +#elif defined ACUTEST_WIN_ + { + HANDLE h; + CONSOLE_SCREEN_BUFFER_INFO info; + WORD attr; + + h = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(h, &info); + + switch(color) { + case ACUTEST_COLOR_RED_: attr = FOREGROUND_RED; break; + case ACUTEST_COLOR_GREEN_: attr = FOREGROUND_GREEN; break; + case ACUTEST_COLOR_YELLOW_: attr = FOREGROUND_RED | FOREGROUND_GREEN; break; + case ACUTEST_COLOR_RED_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break; + case ACUTEST_COLOR_GREEN_INTENSIVE_: attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; + case ACUTEST_COLOR_DEFAULT_INTENSIVE_: attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break; + case ACUTEST_COLOR_YELLOW_INTENSIVE_: attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; break; + default: attr = 0; break; + } + if(attr != 0) + SetConsoleTextAttribute(h, attr); + n = printf("%s", buffer); + SetConsoleTextAttribute(h, info.wAttributes); + return n; + } +#else + n = printf("%s", buffer); + return n; +#endif +} + +static const char* +acutest_basename_(const char* path) +{ + const char* name; + + name = strrchr(path, '/'); + if(name != NULL) + name++; + else + name = path; + +#ifdef ACUTEST_WIN_ + { + const char* alt_name; + + alt_name = strrchr(path, '\\'); + if(alt_name != NULL) + alt_name++; + else + alt_name = path; + + if(alt_name > name) + name = alt_name; + } +#endif + + return name; +} + +static void +acutest_begin_test_line_(const struct acutest_test_* test) +{ + if(!acutest_tap_) { + if(acutest_verbose_level_ >= 3) { + acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s:\n", test->name); + acutest_test_already_logged_++; + } else if(acutest_verbose_level_ >= 1) { + int n; + char spaces[48]; + + n = acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Test %s... ", test->name); + memset(spaces, ' ', sizeof(spaces)); + if(n < (int) sizeof(spaces)) + printf("%.*s", (int) sizeof(spaces) - n, spaces); + } else { + acutest_test_already_logged_ = 1; + } + } +} + +static void +acutest_finish_test_line_(enum acutest_state_ state) +{ + if(acutest_tap_) { + printf("%s %d - %s%s\n", + (state == ACUTEST_STATE_SUCCESS || state == ACUTEST_STATE_SKIPPED) ? "ok" : "not ok", + acutest_current_index_ + 1, + acutest_current_test_->name, + (state == ACUTEST_STATE_SKIPPED) ? " # SKIP" : ""); + + if(state == ACUTEST_STATE_SUCCESS && acutest_timer_) { + printf("# Duration: "); + acutest_timer_print_diff_(); + printf("\n"); + } + } else { + int color; + const char* str; + + switch(state) { + case ACUTEST_STATE_SUCCESS: color = ACUTEST_COLOR_GREEN_INTENSIVE_; str = "OK"; break; + case ACUTEST_STATE_SKIPPED: color = ACUTEST_COLOR_YELLOW_INTENSIVE_; str = "SKIPPED"; break; + case ACUTEST_STATE_FAILED: /* Fall through. */ + default: color = ACUTEST_COLOR_RED_INTENSIVE_; str = "FAILED"; break; + } + + printf("[ "); + acutest_colored_printf_(color, "%s", str); + printf(" ]"); + + if(state == ACUTEST_STATE_SUCCESS && acutest_timer_) { + printf(" "); + acutest_timer_print_diff_(); + } + + printf("\n"); + } +} + +static void +acutest_line_indent_(int level) +{ + static const char spaces[] = " "; + int n = level * 2; + + if(acutest_tap_ && n > 0) { + n--; + printf("#"); + } + + while(n > 16) { + printf("%s", spaces); + n -= 16; + } + printf("%.*s", n, spaces); +} + +void ACUTEST_ATTRIBUTE_(format (printf, 3, 4)) +acutest_skip_(const char* file, int line, const char* fmt, ...) +{ + va_list args; + size_t reason_len; + + va_start(args, fmt); + vsnprintf(acutest_test_skip_reason_, sizeof(acutest_test_skip_reason_), fmt, args); + va_end(args); + acutest_test_skip_reason_[sizeof(acutest_test_skip_reason_)-1] = '\0'; + + /* Remove final dot, if provided; that collides with our other logic. */ + reason_len = strlen(acutest_test_skip_reason_); + if(acutest_test_skip_reason_[reason_len-1] == '.') + acutest_test_skip_reason_[reason_len-1] = '\0'; + + if(acutest_test_check_count_ > 0) { + acutest_check_(0, file, line, "Cannot skip, already performed some checks"); + return; + } + + if(acutest_verbose_level_ >= 2) { + const char *result_str = "skipped"; + int result_color = ACUTEST_COLOR_YELLOW_; + + if(!acutest_test_already_logged_ && acutest_current_test_ != NULL) + acutest_finish_test_line_(ACUTEST_STATE_SKIPPED); + acutest_test_already_logged_++; + + acutest_line_indent_(1); + + if(file != NULL) { + file = acutest_basename_(file); + printf("%s:%d: ", file, line); + } + + printf("%s... ", acutest_test_skip_reason_); + acutest_colored_printf_(result_color, "%s", result_str); + printf("\n"); + acutest_test_already_logged_++; + } + + acutest_test_skip_count_++; +} + +int ACUTEST_ATTRIBUTE_(format (printf, 4, 5)) +acutest_check_(int cond, const char* file, int line, const char* fmt, ...) +{ + const char *result_str; + int result_color; + int verbose_level; + + if(acutest_test_skip_count_) { + /* We've skipped the test. We shouldn't be here: The test implementation + * should have already return before. So lets suppress the following + * output. */ + cond = 1; + goto skip_check; + } + + if(cond) { + result_str = "ok"; + result_color = ACUTEST_COLOR_GREEN_; + verbose_level = 3; + } else { + if(!acutest_test_already_logged_ && acutest_current_test_ != NULL) + acutest_finish_test_line_(ACUTEST_STATE_FAILED); + + acutest_test_failures_++; + acutest_test_already_logged_++; + + result_str = "failed"; + result_color = ACUTEST_COLOR_RED_; + verbose_level = 2; + } + + if(acutest_verbose_level_ >= verbose_level) { + va_list args; + + if(!acutest_case_already_logged_ && acutest_case_name_[0]) { + acutest_line_indent_(1); + acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_); + acutest_test_already_logged_++; + acutest_case_already_logged_++; + } + + acutest_line_indent_(acutest_case_name_[0] ? 2 : 1); + if(file != NULL) { + file = acutest_basename_(file); + printf("%s:%d: ", file, line); + } + + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + + printf("... "); + acutest_colored_printf_(result_color, "%s", result_str); + printf("\n"); + acutest_test_already_logged_++; + } + + acutest_test_check_count_++; + +skip_check: + acutest_cond_failed_ = (cond == 0); + return !acutest_cond_failed_; +} + +void ACUTEST_ATTRIBUTE_(format (printf, 1, 2)) +acutest_case_(const char* fmt, ...) +{ + va_list args; + + if(acutest_verbose_level_ < 2) + return; + + if(acutest_case_name_[0]) { + acutest_case_already_logged_ = 0; + acutest_case_name_[0] = '\0'; + } + + if(fmt == NULL) + return; + + va_start(args, fmt); + vsnprintf(acutest_case_name_, sizeof(acutest_case_name_) - 1, fmt, args); + va_end(args); + acutest_case_name_[sizeof(acutest_case_name_) - 1] = '\0'; + + if(acutest_verbose_level_ >= 3) { + acutest_line_indent_(1); + acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Case %s:\n", acutest_case_name_); + acutest_test_already_logged_++; + acutest_case_already_logged_++; + } +} + +void ACUTEST_ATTRIBUTE_(format (printf, 1, 2)) +acutest_message_(const char* fmt, ...) +{ + char buffer[TEST_MSG_MAXSIZE]; + char* line_beg; + char* line_end; + va_list args; + + if(acutest_verbose_level_ < 2) + return; + + /* We allow extra message only when something is already wrong in the + * current test. */ + if(acutest_current_test_ == NULL || !acutest_cond_failed_) + return; + + va_start(args, fmt); + vsnprintf(buffer, TEST_MSG_MAXSIZE, fmt, args); + va_end(args); + buffer[TEST_MSG_MAXSIZE-1] = '\0'; + + line_beg = buffer; + while(1) { + line_end = strchr(line_beg, '\n'); + if(line_end == NULL) + break; + acutest_line_indent_(acutest_case_name_[0] ? 3 : 2); + printf("%.*s\n", (int)(line_end - line_beg), line_beg); + line_beg = line_end + 1; + } + if(line_beg[0] != '\0') { + acutest_line_indent_(acutest_case_name_[0] ? 3 : 2); + printf("%s\n", line_beg); + } +} + +void +acutest_dump_(const char* title, const void* addr, size_t size) +{ + static const size_t BYTES_PER_LINE = 16; + size_t line_beg; + size_t truncate = 0; + + if(acutest_verbose_level_ < 2) + return; + + /* We allow extra message only when something is already wrong in the + * current test. */ + if(acutest_current_test_ == NULL || !acutest_cond_failed_) + return; + + if(size > TEST_DUMP_MAXSIZE) { + truncate = size - TEST_DUMP_MAXSIZE; + size = TEST_DUMP_MAXSIZE; + } + + acutest_line_indent_(acutest_case_name_[0] ? 3 : 2); + printf((title[strlen(title)-1] == ':') ? "%s\n" : "%s:\n", title); + + for(line_beg = 0; line_beg < size; line_beg += BYTES_PER_LINE) { + size_t line_end = line_beg + BYTES_PER_LINE; + size_t off; + + acutest_line_indent_(acutest_case_name_[0] ? 4 : 3); + printf("%08lx: ", (unsigned long)line_beg); + for(off = line_beg; off < line_end; off++) { + if(off < size) + printf(" %02x", ((const unsigned char*)addr)[off]); + else + printf(" "); + } + + printf(" "); + for(off = line_beg; off < line_end; off++) { + unsigned char byte = ((const unsigned char*)addr)[off]; + if(off < size) + printf("%c", (iscntrl(byte) ? '.' : byte)); + else + break; + } + + printf("\n"); + } + + if(truncate > 0) { + acutest_line_indent_(acutest_case_name_[0] ? 4 : 3); + printf(" ... (and more %u bytes)\n", (unsigned) truncate); + } +} + +/* This is called just before each test */ +static void +acutest_init_(const char *test_name) +{ +#ifdef TEST_INIT + TEST_INIT + ; /* Allow for a single unterminated function call */ +#endif + + /* Suppress any warnings about unused variable. */ + (void) test_name; +} + +/* This is called after each test */ +static void +acutest_fini_(const char *test_name) +{ +#ifdef TEST_FINI + TEST_FINI + ; /* Allow for a single unterminated function call */ +#endif + + /* Suppress any warnings about unused variable. */ + (void) test_name; +} + +void +acutest_abort_(void) +{ + if(acutest_abort_has_jmp_buf_) { + longjmp(acutest_abort_jmp_buf_, 1); + } else { + if(acutest_current_test_ != NULL) + acutest_fini_(acutest_current_test_->name); + fflush(stdout); + fflush(stderr); + acutest_exit_(ACUTEST_STATE_FAILED); + } +} + +static void +acutest_list_names_(void) +{ + const struct acutest_test_* test; + + printf("Unit tests:\n"); + for(test = ´st_list_[0]; test->func != NULL; test++) + printf(" %s\n", test->name); +} + +static int +acutest_name_contains_word_(const char* name, const char* pattern) +{ + static const char word_delim[] = " \t-_/.,:;"; + const char* substr; + size_t pattern_len; + + pattern_len = strlen(pattern); + + substr = strstr(name, pattern); + while(substr != NULL) { + int starts_on_word_boundary = (substr == name || strchr(word_delim, substr[-1]) != NULL); + int ends_on_word_boundary = (substr[pattern_len] == '\0' || strchr(word_delim, substr[pattern_len]) != NULL); + + if(starts_on_word_boundary && ends_on_word_boundary) + return 1; + + substr = strstr(substr+1, pattern); + } + + return 0; +} + +static int +acutest_select_(const char* pattern) +{ + int i; + int n = 0; + + /* Try exact match. */ + for(i = 0; i < acutest_list_size_; i++) { + if(strcmp(acutest_list_[i].name, pattern) == 0) { + acutest_test_data_[i].state = ACUTEST_STATE_SELECTED; + n++; + break; + } + } + if(n > 0) + return n; + + /* Try word match. */ + for(i = 0; i < acutest_list_size_; i++) { + if(acutest_name_contains_word_(acutest_list_[i].name, pattern)) { + acutest_test_data_[i].state = ACUTEST_STATE_SELECTED; + n++; + } + } + if(n > 0) + return n; + + /* Try relaxed match. */ + for(i = 0; i < acutest_list_size_; i++) { + if(strstr(acutest_list_[i].name, pattern) != NULL) { + acutest_test_data_[i].state = ACUTEST_STATE_SELECTED; + n++; + } + } + + return n; +} + + +/* Called if anything goes bad in Acutest, or if the unit test ends in other + * way then by normal returning from its function (e.g. exception or some + * abnormal child process termination). */ +static void ACUTEST_ATTRIBUTE_(format (printf, 1, 2)) +acutest_error_(const char* fmt, ...) +{ + if(acutest_verbose_level_ == 0) + return; + + if(acutest_verbose_level_ >= 2) { + va_list args; + + acutest_line_indent_(1); + if(acutest_verbose_level_ >= 3) + acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "ERROR: "); + va_start(args, fmt); + vprintf(fmt, args); + va_end(args); + printf("\n"); + } + + if(acutest_verbose_level_ >= 3) { + printf("\n"); + } +} + +/* Call directly the given test unit function. */ +static enum acutest_state_ +acutest_do_run_(const struct acutest_test_* test, int index) +{ + enum acutest_state_ state = ACUTEST_STATE_FAILED; + + acutest_current_test_ = test; + acutest_current_index_ = index; + acutest_test_failures_ = 0; + acutest_test_already_logged_ = 0; + acutest_test_check_count_ = 0; + acutest_test_skip_count_ = 0; + acutest_cond_failed_ = 0; + +#ifdef __cplusplus +#ifndef TEST_NO_EXCEPTIONS + try { +#endif +#endif + acutest_init_(test->name); + acutest_begin_test_line_(test); + + /* This is good to do in case the test unit crashes. */ + fflush(stdout); + fflush(stderr); + + if(!acutest_worker_) { + acutest_abort_has_jmp_buf_ = 1; + if(setjmp(acutest_abort_jmp_buf_) != 0) + goto aborted; + } + + acutest_timer_get_time_(´st_timer_start_); + test->func(); + +aborted: + acutest_abort_has_jmp_buf_ = 0; + acutest_timer_get_time_(´st_timer_end_); + + if(acutest_test_failures_ > 0) + state = ACUTEST_STATE_FAILED; + else if(acutest_test_skip_count_ > 0) + state = ACUTEST_STATE_SKIPPED; + else + state = ACUTEST_STATE_SUCCESS; + + if(!acutest_test_already_logged_) + acutest_finish_test_line_(state); + + if(acutest_verbose_level_ >= 3) { + acutest_line_indent_(1); + switch(state) { + case ACUTEST_STATE_SUCCESS: + acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS: "); + printf("All conditions have passed.\n"); + + if(acutest_timer_) { + acutest_line_indent_(1); + printf("Duration: "); + acutest_timer_print_diff_(); + printf("\n"); + } + break; + + case ACUTEST_STATE_SKIPPED: + acutest_colored_printf_(ACUTEST_COLOR_YELLOW_INTENSIVE_, "SKIPPED: "); + printf("%s.\n", acutest_test_skip_reason_); + break; + + default: + acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); + printf("%d condition%s %s failed.\n", + acutest_test_failures_, + (acutest_test_failures_ == 1) ? "" : "s", + (acutest_test_failures_ == 1) ? "has" : "have"); + break; + } + printf("\n"); + } + +#ifdef __cplusplus +#ifndef TEST_NO_EXCEPTIONS + } catch(std::exception& e) { + const char* what = e.what(); + acutest_check_(0, NULL, 0, "Threw std::exception"); + if(what != NULL) + acutest_message_("std::exception::what(): %s", what); + + if(acutest_verbose_level_ >= 3) { + acutest_line_indent_(1); + acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); + printf("C++ exception.\n\n"); + } + } catch(...) { + acutest_check_(0, NULL, 0, "Threw an exception"); + + if(acutest_verbose_level_ >= 3) { + acutest_line_indent_(1); + acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED: "); + printf("C++ exception.\n\n"); + } + } +#endif +#endif + + acutest_fini_(test->name); + acutest_case_(NULL); + acutest_current_test_ = NULL; + + return state; +} + +/* Trigger the unit test. If possible (and not suppressed) it starts a child + * process who calls acutest_do_run_(), otherwise it calls acutest_do_run_() + * directly. */ +static void +acutest_run_(const struct acutest_test_* test, int index, int master_index) +{ + enum acutest_state_ state = ACUTEST_STATE_FAILED; + acutest_timer_type_ start, end; + + acutest_current_test_ = test; + acutest_test_already_logged_ = 0; + acutest_timer_get_time_(&start); + + if(!acutest_no_exec_) { + +#if defined(ACUTEST_UNIX_) + + pid_t pid; + int exit_code; + + /* Make sure the child starts with empty I/O buffers. */ + fflush(stdout); + fflush(stderr); + + pid = fork(); + if(pid == (pid_t)-1) { + acutest_error_("Cannot fork. %s [%d]", strerror(errno), errno); + } else if(pid == 0) { + /* Child: Do the test. */ + acutest_worker_ = 1; + state = acutest_do_run_(test, index); + acutest_exit_((int) state); + } else { + /* Parent: Wait until child terminates and analyze its exit code. */ + waitpid(pid, &exit_code, 0); + if(WIFEXITED(exit_code)) { + state = (enum acutest_state_) WEXITSTATUS(exit_code); + } else if(WIFSIGNALED(exit_code)) { + char tmp[32]; + const char* signame; + switch(WTERMSIG(exit_code)) { + case SIGINT: signame = "SIGINT"; break; + case SIGHUP: signame = "SIGHUP"; break; + case SIGQUIT: signame = "SIGQUIT"; break; + case SIGABRT: signame = "SIGABRT"; break; + case SIGKILL: signame = "SIGKILL"; break; + case SIGSEGV: signame = "SIGSEGV"; break; + case SIGILL: signame = "SIGILL"; break; + case SIGTERM: signame = "SIGTERM"; break; + default: snprintf(tmp, sizeof(tmp), "signal %d", WTERMSIG(exit_code)); signame = tmp; break; + } + acutest_error_("Test interrupted by %s.", signame); + } else { + acutest_error_("Test ended in an unexpected way [%d].", exit_code); + } + } + +#elif defined(ACUTEST_WIN_) + + char buffer[512] = {0}; + STARTUPINFOA startupInfo; + PROCESS_INFORMATION processInfo; + DWORD exitCode; + + /* Windows has no fork(). So we propagate all info into the child + * through a command line arguments. */ + snprintf(buffer, sizeof(buffer), + "%s --worker=%d %s --no-exec --no-summary %s --verbose=%d --color=%s -- \"%s\"", + acutest_argv0_, index, acutest_timer_ ? "--time" : "", + acutest_tap_ ? "--tap" : "", acutest_verbose_level_, + acutest_colorize_ ? "always" : "never", + test->name); + memset(&startupInfo, 0, sizeof(startupInfo)); + startupInfo.cb = sizeof(STARTUPINFO); + if(CreateProcessA(NULL, buffer, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) { + WaitForSingleObject(processInfo.hProcess, INFINITE); + GetExitCodeProcess(processInfo.hProcess, &exitCode); + CloseHandle(processInfo.hThread); + CloseHandle(processInfo.hProcess); + switch(exitCode) { + case 0: state = ACUTEST_STATE_SUCCESS; break; + case 1: state = ACUTEST_STATE_FAILED; break; + case 2: state = ACUTEST_STATE_SKIPPED; break; + case 3: acutest_error_("Aborted."); break; + case 0xC0000005: acutest_error_("Access violation."); break; + default: acutest_error_("Test ended in an unexpected way [%lu].", exitCode); break; + } + } else { + acutest_error_("Cannot create unit test subprocess [%ld].", GetLastError()); + } + +#else + + /* A platform where we don't know how to run child process. */ + state = acutest_do_run_(test, index); + +#endif + + } else { + /* Child processes suppressed through --no-exec. */ + state = acutest_do_run_(test, index); + } + acutest_timer_get_time_(&end); + + acutest_current_test_ = NULL; + + acutest_test_data_[master_index].state = state; + acutest_test_data_[master_index].duration = acutest_timer_diff_(start, end); +} + +#if defined(ACUTEST_WIN_) +/* Callback for SEH events. */ +static LONG CALLBACK +acutest_seh_exception_filter_(EXCEPTION_POINTERS *ptrs) +{ + acutest_check_(0, NULL, 0, "Unhandled SEH exception"); + acutest_message_("Exception code: 0x%08lx", ptrs->ExceptionRecord->ExceptionCode); + acutest_message_("Exception address: 0x%p", ptrs->ExceptionRecord->ExceptionAddress); + + fflush(stdout); + fflush(stderr); + + return EXCEPTION_EXECUTE_HANDLER; +} +#endif + + +#define ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ 0x0001 +#define ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ 0x0002 + +#define ACUTEST_CMDLINE_OPTID_NONE_ 0 +#define ACUTEST_CMDLINE_OPTID_UNKNOWN_ (-0x7fffffff + 0) +#define ACUTEST_CMDLINE_OPTID_MISSINGARG_ (-0x7fffffff + 1) +#define ACUTEST_CMDLINE_OPTID_BOGUSARG_ (-0x7fffffff + 2) + +typedef struct acutest_test_CMDLINE_OPTION_ { + char shortname; + const char* longname; + int id; + unsigned flags; +} ACUTEST_CMDLINE_OPTION_; + +static int +acutest_cmdline_handle_short_opt_group_(const ACUTEST_CMDLINE_OPTION_* options, + const char* arggroup, + int (*callback)(int /*optval*/, const char* /*arg*/)) +{ + const ACUTEST_CMDLINE_OPTION_* opt; + int i; + int ret = 0; + + for(i = 0; arggroup[i] != '\0'; i++) { + for(opt = options; opt->id != 0; opt++) { + if(arggroup[i] == opt->shortname) + break; + } + + if(opt->id != 0 && !(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) { + ret = callback(opt->id, NULL); + } else { + /* Unknown option. */ + char badoptname[3]; + badoptname[0] = '-'; + badoptname[1] = arggroup[i]; + badoptname[2] = '\0'; + ret = callback((opt->id != 0 ? ACUTEST_CMDLINE_OPTID_MISSINGARG_ : ACUTEST_CMDLINE_OPTID_UNKNOWN_), + badoptname); + } + + if(ret != 0) + break; + } + + return ret; +} + +#define ACUTEST_CMDLINE_AUXBUF_SIZE_ 32 + +static int +acutest_cmdline_read_(const ACUTEST_CMDLINE_OPTION_* options, int argc, char** argv, + int (*callback)(int /*optval*/, const char* /*arg*/)) +{ + + const ACUTEST_CMDLINE_OPTION_* opt; + char auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_+1]; + int after_doubledash = 0; + int i = 1; + int ret = 0; + + auxbuf[ACUTEST_CMDLINE_AUXBUF_SIZE_] = '\0'; + + while(i < argc) { + if(after_doubledash || strcmp(argv[i], "-") == 0) { + /* Non-option argument. */ + ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]); + } else if(strcmp(argv[i], "--") == 0) { + /* End of options. All the remaining members are non-option arguments. */ + after_doubledash = 1; + } else if(argv[i][0] != '-') { + /* Non-option argument. */ + ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]); + } else { + for(opt = options; opt->id != 0; opt++) { + if(opt->longname != NULL && strncmp(argv[i], "--", 2) == 0) { + size_t len = strlen(opt->longname); + if(strncmp(argv[i]+2, opt->longname, len) == 0) { + /* Regular long option. */ + if(argv[i][2+len] == '\0') { + /* with no argument provided. */ + if(!(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) + ret = callback(opt->id, NULL); + else + ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]); + break; + } else if(argv[i][2+len] == '=') { + /* with an argument provided. */ + if(opt->flags & (ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ | ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_)) { + ret = callback(opt->id, argv[i]+2+len+1); + } else { + snprintf(auxbuf, sizeof(auxbuf), "--%s", opt->longname); + ret = callback(ACUTEST_CMDLINE_OPTID_BOGUSARG_, auxbuf); + } + break; + } else { + continue; + } + } + } else if(opt->shortname != '\0' && argv[i][0] == '-') { + if(argv[i][1] == opt->shortname) { + /* Regular short option. */ + if(opt->flags & ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_) { + if(argv[i][2] != '\0') + ret = callback(opt->id, argv[i]+2); + else if(i+1 < argc) + ret = callback(opt->id, argv[++i]); + else + ret = callback(ACUTEST_CMDLINE_OPTID_MISSINGARG_, argv[i]); + break; + } else { + ret = callback(opt->id, NULL); + + /* There might be more (argument-less) short options + * grouped together. */ + if(ret == 0 && argv[i][2] != '\0') + ret = acutest_cmdline_handle_short_opt_group_(options, argv[i]+2, callback); + break; + } + } + } + } + + if(opt->id == 0) { /* still not handled? */ + if(argv[i][0] != '-') { + /* Non-option argument. */ + ret = callback(ACUTEST_CMDLINE_OPTID_NONE_, argv[i]); + } else { + /* Unknown option. */ + char* badoptname = argv[i]; + + if(strncmp(badoptname, "--", 2) == 0) { + /* Strip any argument from the long option. */ + char* assignment = strchr(badoptname, '='); + if(assignment != NULL) { + size_t len = (size_t)(assignment - badoptname); + if(len > ACUTEST_CMDLINE_AUXBUF_SIZE_) + len = ACUTEST_CMDLINE_AUXBUF_SIZE_; + strncpy(auxbuf, badoptname, len); + auxbuf[len] = '\0'; + badoptname = auxbuf; + } + } + + ret = callback(ACUTEST_CMDLINE_OPTID_UNKNOWN_, badoptname); + } + } + } + + if(ret != 0) + return ret; + i++; + } + + return ret; +} + +static void +acutest_help_(void) +{ + printf("Usage: %s [options] [test...]\n", acutest_argv0_); + printf("\n"); + printf("Run the specified unit tests; or if the option '--exclude' is used, run all\n"); + printf("tests in the suite but those listed. By default, if no tests are specified\n"); + printf("on the command line, all unit tests in the suite are run.\n"); + printf("\n"); + printf("Options:\n"); + printf(" -X, --exclude Execute all unit tests but the listed ones\n"); + printf(" --exec[=WHEN] If supported, execute unit tests as child processes\n"); + printf(" (WHEN is one of 'auto', 'always', 'never')\n"); + printf(" -E, --no-exec Same as --exec=never\n"); +#if defined ACUTEST_WIN_ + printf(" -t, --time Measure test duration\n"); +#elif defined ACUTEST_HAS_POSIX_TIMER_ + printf(" -t, --time Measure test duration (real time)\n"); + printf(" --time=TIMER Measure test duration, using given timer\n"); + printf(" (TIMER is one of 'real', 'cpu')\n"); +#endif + printf(" --no-summary Suppress printing of test results summary\n"); + printf(" --tap Produce TAP-compliant output\n"); + printf(" (See https://testanything.org/)\n"); + printf(" -x, --xml-output=FILE Enable XUnit output to the given file\n"); + printf(" -l, --list List unit tests in the suite and exit\n"); + printf(" -v, --verbose Make output more verbose\n"); + printf(" --verbose=LEVEL Set verbose level to LEVEL:\n"); + printf(" 0 ... Be silent\n"); + printf(" 1 ... Output one line per test (and summary)\n"); + printf(" 2 ... As 1 and failed conditions (this is default)\n"); + printf(" 3 ... As 1 and all conditions (and extended summary)\n"); + printf(" -q, --quiet Same as --verbose=0\n"); + printf(" --color[=WHEN] Enable colorized output\n"); + printf(" (WHEN is one of 'auto', 'always', 'never')\n"); + printf(" --no-color Same as --color=never\n"); + printf(" -h, --help Display this help and exit\n"); + + if(acutest_list_size_ < 16) { + printf("\n"); + acutest_list_names_(); + } +} + +static const ACUTEST_CMDLINE_OPTION_ acutest_cmdline_options_[] = { + { 'X', "exclude", 'X', 0 }, + { 's', "skip", 'X', 0 }, /* kept for compatibility, use --exclude instead */ + { 0, "exec", 'e', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, + { 'E', "no-exec", 'E', 0 }, +#if defined ACUTEST_WIN_ + { 't', "time", 't', 0 }, + { 0, "timer", 't', 0 }, /* kept for compatibility */ +#elif defined ACUTEST_HAS_POSIX_TIMER_ + { 't', "time", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, + { 0, "timer", 't', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, /* kept for compatibility */ +#endif + { 0, "no-summary", 'S', 0 }, + { 0, "tap", 'T', 0 }, + { 'l', "list", 'l', 0 }, + { 'v', "verbose", 'v', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, + { 'q', "quiet", 'q', 0 }, + { 0, "color", 'c', ACUTEST_CMDLINE_OPTFLAG_OPTIONALARG_ }, + { 0, "no-color", 'C', 0 }, + { 'h', "help", 'h', 0 }, + { 0, "worker", 'w', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, /* internal */ + { 'x', "xml-output", 'x', ACUTEST_CMDLINE_OPTFLAG_REQUIREDARG_ }, + { 0, NULL, 0, 0 } +}; + +static int +acutest_cmdline_callback_(int id, const char* arg) +{ + switch(id) { + case 'X': + acutest_exclude_mode_ = 1; + break; + + case 'e': + if(arg == NULL || strcmp(arg, "always") == 0) { + acutest_no_exec_ = 0; + } else if(strcmp(arg, "never") == 0) { + acutest_no_exec_ = 1; + } else if(strcmp(arg, "auto") == 0) { + /*noop*/ + } else { + fprintf(stderr, "%s: Unrecognized argument '%s' for option --exec.\n", acutest_argv0_, arg); + fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); + acutest_exit_(2); + } + break; + + case 'E': + acutest_no_exec_ = 1; + break; + + case 't': +#if defined ACUTEST_WIN_ || defined ACUTEST_HAS_POSIX_TIMER_ + if(arg == NULL || strcmp(arg, "real") == 0) { + acutest_timer_ = 1; + #ifndef ACUTEST_WIN_ + } else if(strcmp(arg, "cpu") == 0) { + acutest_timer_ = 2; + #endif + } else { + fprintf(stderr, "%s: Unrecognized argument '%s' for option --time.\n", acutest_argv0_, arg); + fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); + acutest_exit_(2); + } +#endif + break; + + case 'S': + acutest_no_summary_ = 1; + break; + + case 'T': + acutest_tap_ = 1; + break; + + case 'l': + acutest_list_names_(); + acutest_exit_(0); + break; + + case 'v': + acutest_verbose_level_ = (arg != NULL ? atoi(arg) : acutest_verbose_level_+1); + break; + + case 'q': + acutest_verbose_level_ = 0; + break; + + case 'c': + if(arg == NULL || strcmp(arg, "always") == 0) { + acutest_colorize_ = 1; + } else if(strcmp(arg, "never") == 0) { + acutest_colorize_ = 0; + } else if(strcmp(arg, "auto") == 0) { + /*noop*/ + } else { + fprintf(stderr, "%s: Unrecognized argument '%s' for option --color.\n", acutest_argv0_, arg); + fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); + acutest_exit_(2); + } + break; + + case 'C': + acutest_colorize_ = 0; + break; + + case 'h': + acutest_help_(); + acutest_exit_(0); + break; + + case 'w': + acutest_worker_ = 1; + acutest_worker_index_ = atoi(arg); + break; + case 'x': + acutest_xml_output_ = fopen(arg, "w"); + if (!acutest_xml_output_) { + fprintf(stderr, "Unable to open '%s': %s\n", arg, strerror(errno)); + acutest_exit_(2); + } + break; + + case 0: + if(acutest_select_(arg) == 0) { + fprintf(stderr, "%s: Unrecognized unit test '%s'\n", acutest_argv0_, arg); + fprintf(stderr, "Try '%s --list' for list of unit tests.\n", acutest_argv0_); + acutest_exit_(2); + } + break; + + case ACUTEST_CMDLINE_OPTID_UNKNOWN_: + fprintf(stderr, "Unrecognized command line option '%s'.\n", arg); + fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); + acutest_exit_(2); + break; + + case ACUTEST_CMDLINE_OPTID_MISSINGARG_: + fprintf(stderr, "The command line option '%s' requires an argument.\n", arg); + fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); + acutest_exit_(2); + break; + + case ACUTEST_CMDLINE_OPTID_BOGUSARG_: + fprintf(stderr, "The command line option '%s' does not expect an argument.\n", arg); + fprintf(stderr, "Try '%s --help' for more information.\n", acutest_argv0_); + acutest_exit_(2); + break; + } + + return 0; +} + +static int +acutest_under_debugger_(void) +{ +#ifdef ACUTEST_LINUX_ + /* Scan /proc/self/status for line "TracerPid: [PID]". If such line exists + * and the PID is non-zero, we're being debugged. */ + { + static const int OVERLAP = 32; + int fd; + char buf[512]; + size_t n_read; + pid_t tracer_pid = 0; + + /* Little trick so that we can treat the 1st line the same as any other + * and detect line start easily. */ + buf[0] = '\n'; + n_read = 1; + + fd = open("/proc/self/status", O_RDONLY); + if(fd != -1) { + while(1) { + static const char pattern[] = "\nTracerPid:"; + const char* field; + + while(n_read < sizeof(buf) - 1) { + ssize_t n; + + n = read(fd, buf + n_read, sizeof(buf) - 1 - n_read); + if(n <= 0) + break; + n_read += (size_t)n; + } + buf[n_read] = '\0'; + + field = strstr(buf, pattern); + if(field != NULL && field < buf + sizeof(buf) - OVERLAP) { + tracer_pid = (pid_t) atoi(field + sizeof(pattern) - 1); + break; + } + + if(n_read == sizeof(buf) - 1) { + /* Move the tail with the potentially incomplete line we're + * be looking for to the beginning of the buffer. + * (The OVERLAP must be large enough so the searched line + * can fit in completely.) */ + memmove(buf, buf + sizeof(buf) - 1 - OVERLAP, OVERLAP); + n_read = OVERLAP; + } else { + break; + } + } + + close(fd); + + if(tracer_pid != 0) + return 1; + } + } +#endif + +#ifdef ACUTEST_MACOS_ + /* See https://developer.apple.com/library/archive/qa/qa1361/_index.html */ + { + int mib[4]; + struct kinfo_proc info; + size_t size; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_PID; + mib[3] = getpid(); + + size = sizeof(info); + info.kp_proc.p_flag = 0; + sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0); + + if(info.kp_proc.p_flag & P_TRACED) + return 1; + } +#endif + +#ifdef ACUTEST_WIN_ + if(IsDebuggerPresent()) + return 1; +#endif + +#ifdef RUNNING_ON_VALGRIND + /* We treat Valgrind as a debugger of sorts. + * (Macro RUNNING_ON_VALGRIND is provided by , if available.) */ + if(RUNNING_ON_VALGRIND) + return 1; +#endif + + return 0; +} + +int +main(int argc, char** argv) +{ + int i, index; + int exit_code = 1; + + acutest_argv0_ = argv[0]; + +#if defined ACUTEST_UNIX_ + acutest_colorize_ = isatty(STDOUT_FILENO); +#elif defined ACUTEST_WIN_ + #if defined _BORLANDC_ + acutest_colorize_ = isatty(_fileno(stdout)); + #else + acutest_colorize_ = _isatty(_fileno(stdout)); + #endif +#else + acutest_colorize_ = 0; +#endif + + /* Count all test units */ + acutest_list_size_ = 0; + for(i = 0; acutest_list_[i].func != NULL; i++) + acutest_list_size_++; + + acutest_test_data_ = (struct acutest_test_data_*)calloc(acutest_list_size_, sizeof(struct acutest_test_data_)); + if(acutest_test_data_ == NULL) { + fprintf(stderr, "Out of memory.\n"); + acutest_exit_(2); + } + + /* Parse options */ + acutest_cmdline_read_(acutest_cmdline_options_, argc, argv, acutest_cmdline_callback_); + + /* Initialize the proper timer. */ + acutest_timer_init_(); + +#if defined(ACUTEST_WIN_) + SetUnhandledExceptionFilter(acutest_seh_exception_filter_); +#ifdef _MSC_VER + _set_abort_behavior(0, _WRITE_ABORT_MSG); +#endif +#endif + + /* Determine what to run. */ + if(acutest_count_(ACUTEST_STATE_SELECTED) > 0) { + enum acutest_state_ if_selected; + enum acutest_state_ if_unselected; + + if(!acutest_exclude_mode_) { + if_selected = ACUTEST_STATE_NEEDTORUN; + if_unselected = ACUTEST_STATE_EXCLUDED; + } else { + if_selected = ACUTEST_STATE_EXCLUDED; + if_unselected = ACUTEST_STATE_NEEDTORUN; + } + + for(i = 0; acutest_list_[i].func != NULL; i++) { + if(acutest_test_data_[i].state == ACUTEST_STATE_SELECTED) + acutest_test_data_[i].state = if_selected; + else + acutest_test_data_[i].state = if_unselected; + } + } else { + /* By default, we want to run all tests. */ + for(i = 0; acutest_list_[i].func != NULL; i++) + acutest_test_data_[i].state = ACUTEST_STATE_NEEDTORUN; + } + + /* By default, we want to suppress running tests as child processes if we + * run just one test, or if we're under debugger: Debugging tests is then + * so much easier. */ + if(acutest_no_exec_ < 0) { + if(acutest_count_(ACUTEST_STATE_NEEDTORUN) <= 1 || acutest_under_debugger_()) + acutest_no_exec_ = 1; + else + acutest_no_exec_ = 0; + } + + if(acutest_tap_) { + /* TAP requires we know test result ("ok", "not ok") before we output + * anything about the test, and this gets problematic for larger verbose + * levels. */ + if(acutest_verbose_level_ > 2) + acutest_verbose_level_ = 2; + + /* TAP harness should provide some summary. */ + acutest_no_summary_ = 1; + + if(!acutest_worker_) + printf("1..%d\n", acutest_count_(ACUTEST_STATE_NEEDTORUN)); + } + + index = acutest_worker_index_; + for(i = 0; acutest_list_[i].func != NULL; i++) { + if(acutest_test_data_[i].state == ACUTEST_STATE_NEEDTORUN) + acutest_run_(´st_list_[i], index++, i); + } + + /* Write a summary */ + if(!acutest_no_summary_ && acutest_verbose_level_ >= 1) { + int n_run, n_success, n_failed ; + + n_run = acutest_list_size_ - acutest_count_(ACUTEST_STATE_EXCLUDED); + n_success = acutest_count_(ACUTEST_STATE_SUCCESS); + n_failed = acutest_count_(ACUTEST_STATE_FAILED); + + if(acutest_verbose_level_ >= 3) { + acutest_colored_printf_(ACUTEST_COLOR_DEFAULT_INTENSIVE_, "Summary:\n"); + + printf(" Count of run unit tests: %4d\n", n_run); + printf(" Count of successful unit tests: %4d\n", n_success); + printf(" Count of failed unit tests: %4d\n", n_failed); + } + + if(n_failed == 0) { + acutest_colored_printf_(ACUTEST_COLOR_GREEN_INTENSIVE_, "SUCCESS:"); + printf(" No unit tests have failed.\n"); + } else { + acutest_colored_printf_(ACUTEST_COLOR_RED_INTENSIVE_, "FAILED:"); + printf(" %d of %d unit tests %s failed.\n", + n_failed, n_run, (n_failed == 1) ? "has" : "have"); + } + + if(acutest_verbose_level_ >= 3) + printf("\n"); + } + + if (acutest_xml_output_) { + const char* suite_name = acutest_basename_(argv[0]); + fprintf(acutest_xml_output_, "\n"); + fprintf(acutest_xml_output_, "\n", + suite_name, + (int)acutest_list_size_, + acutest_count_(ACUTEST_STATE_FAILED), + acutest_count_(ACUTEST_STATE_SKIPPED) + acutest_count_(ACUTEST_STATE_EXCLUDED)); + for(i = 0; acutest_list_[i].func != NULL; i++) { + struct acutest_test_data_ *details = ´st_test_data_[i]; + const char* str_state; + fprintf(acutest_xml_output_, " \n", acutest_list_[i].name, details->duration); + + switch(details->state) { + case ACUTEST_STATE_SUCCESS: str_state = NULL; break; + case ACUTEST_STATE_EXCLUDED: /* Fall through. */ + case ACUTEST_STATE_SKIPPED: str_state = ""; break; + case ACUTEST_STATE_FAILED: /* Fall through. */ + default: str_state = ""; break; + } + + if(str_state != NULL) + fprintf(acutest_xml_output_, " %s\n", str_state); + fprintf(acutest_xml_output_, " \n"); + } + fprintf(acutest_xml_output_, "\n"); + fclose(acutest_xml_output_); + } + + if(acutest_worker_ && acutest_count_(ACUTEST_STATE_EXCLUDED)+1 == acutest_list_size_) { + /* If we are the child process, we need to propagate the test state + * without any moderation. */ + for(i = 0; acutest_list_[i].func != NULL; i++) { + if(acutest_test_data_[i].state != ACUTEST_STATE_EXCLUDED) { + exit_code = (int) acutest_test_data_[i].state; + break; + } + } + } else { + if(acutest_count_(ACUTEST_STATE_FAILED) > 0) + exit_code = 1; + else + exit_code = 0; + } + + acutest_cleanup_(); + return exit_code; +} + +#ifdef __cplusplus + } /* extern "C" */ +#endif +#endif /* #ifndef TEST_NO_MAIN */ + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + + + +#endif /* #ifndef ACUTEST_H */ diff --git a/tests/acutest_main.c b/tests/acutest_main.c deleted file mode 100644 index 8797732..0000000 --- a/tests/acutest_main.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "../external/acutest/include/acutest.h" - -// This translation unit provides the single main() for Acutest. diff --git a/tests/hot_reload.cpp b/tests/hot_reload.cpp new file mode 100644 index 0000000..40a2903 --- /dev/null +++ b/tests/hot_reload.cpp @@ -0,0 +1,41 @@ +#define TEST_NO_MAIN +#include "acutest.h" +#include +#include +#include +#include +#include +#include "../include/HotReload.h" +#include "physfs_test_utils.h" + +using namespace std::chrono_literals; + +void test_hotreload_directory_watch(void) { + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_watch"; + std::filesystem::create_directories(tmpDir); + + std::filesystem::path f = tmpDir / "a.as"; + std::ofstream ofs(f); + ofs << "// test" << std::endl; + ofs.close(); + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "tmp_watch") == true); + + HotReload hr("tmp_watch"); + TEST_CHECK(hr.CheckForChanges() == false); + + std::this_thread::sleep_for(1s); + std::ofstream ofs2(f, std::ios::app); + ofs2 << "// changed" << std::endl; + ofs2.close(); + + TEST_CHECK(hr.CheckForChanges() == true); + + ShutdownPhysFSTest(); + + std::filesystem::remove(f); + std::filesystem::remove(tmpDir); +} + + diff --git a/tests/hot_reload.h b/tests/hot_reload.h new file mode 100644 index 0000000..1feedfd --- /dev/null +++ b/tests/hot_reload.h @@ -0,0 +1,3 @@ +#pragma once + +void test_hotreload_directory_watch(void); \ No newline at end of file diff --git a/tests/math_bindings_tests.cpp b/tests/math_bindings_tests.cpp new file mode 100644 index 0000000..d1269c5 --- /dev/null +++ b/tests/math_bindings_tests.cpp @@ -0,0 +1,69 @@ +#define TEST_NO_MAIN +#include "acutest.h" +#include +#include +#include +#include "../include/scripting/ScriptEngine.h" +#include "physfs_test_utils.h" + +void test_math_bindings(void) { + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_math"; + std::filesystem::create_directories(tmpDir); + std::filesystem::path scriptPath = tmpDir / "math_test.as"; + + { + std::ofstream script(scriptPath); + script << "float testSin = Math::Sin(1.57079632679f);\n"; + script << "float testCos = Math::Cos(0.0f);\n"; + script << "float testTan = Math::Tan(0.78539816339f);\n"; + script << "float testSqrt = Math::Sqrt(25.0f);\n"; + script << "float testPow = Math::Pow(2.0f, 8.0f);\n"; + script << "float testAbs = Math::Abs(-42.5f);\n"; + } + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + ScriptEngine se; + TEST_CHECK(se.Initialize() == true); + bool compiled = se.CompileScript("math_test.as"); + TEST_CHECK(compiled == true); + + asIScriptEngine* engine = se.GetEngine(); + TEST_CHECK(engine != nullptr); + asIScriptModule* module = engine ? engine->GetModule("main") : nullptr; + TEST_CHECK(module != nullptr); + + auto fetchValue = [module](const char* name) -> float { + int idx = module->GetGlobalVarIndexByName(name); + TEST_CHECK_(idx >= 0, "Global '%s' not found", name); + if (idx < 0) + return 0.0f; + void* address = module->GetAddressOfGlobalVar(idx); + TEST_CHECK_(address != nullptr, "Global '%s' address null", name); + if (!address) + return 0.0f; + return *static_cast(address); + }; + + const float sinVal = fetchValue("testSin"); + const float cosVal = fetchValue("testCos"); + const float tanVal = fetchValue("testTan"); + const float sqrtVal = fetchValue("testSqrt"); + const float powVal = fetchValue("testPow"); + const float absVal = fetchValue("testAbs"); + + TEST_CHECK(std::fabs(sinVal - 1.0f) < 1e-4f); + TEST_CHECK(std::fabs(cosVal - 1.0f) < 1e-4f); + TEST_CHECK(std::fabs(tanVal - 1.0f) < 1e-3f); + TEST_CHECK(std::fabs(sqrtVal - 5.0f) < 1e-4f); + TEST_CHECK(std::fabs(powVal - 256.0f) < 1e-3f); + TEST_CHECK(std::fabs(absVal - 42.5f) < 1e-4f); + + se.Shutdown(); + + ShutdownPhysFSTest(); + + std::filesystem::remove(scriptPath); + std::filesystem::remove(tmpDir); +} diff --git a/tests/math_bindings_tests.h b/tests/math_bindings_tests.h new file mode 100644 index 0000000..c8d1b08 --- /dev/null +++ b/tests/math_bindings_tests.h @@ -0,0 +1,3 @@ +#pragma once + +void test_math_bindings(void); diff --git a/tests/physfs_test_utils.cpp b/tests/physfs_test_utils.cpp new file mode 100644 index 0000000..f293dd4 --- /dev/null +++ b/tests/physfs_test_utils.cpp @@ -0,0 +1,31 @@ +#include "physfs_test_utils.h" +#include "PhysFSManager.h" + +static bool g_initialized = false; + +bool InitPhysFSTest(const std::filesystem::path& writeDir) { + if (!g_initialized) { + if (!PhysFSManager::Initialize("unit_tests")) { + return false; + } + g_initialized = true; + } + + PhysFSManager::SetWriteDir(writeDir.string()); + return true; +} + +bool MountPhysFSTest(const std::filesystem::path& realPath, const std::string& mountPoint) { + return PhysFSManager::MountPath(realPath.string(), mountPoint, true); +} + +bool UnmountPhysFSTest(const std::filesystem::path& realPath) { + return PhysFSManager::UnmountPath(realPath.string()); +} + +void ShutdownPhysFSTest() { + if (g_initialized) { + PhysFSManager::Shutdown(); + g_initialized = false; + } +} diff --git a/tests/physfs_test_utils.h b/tests/physfs_test_utils.h new file mode 100644 index 0000000..cd66965 --- /dev/null +++ b/tests/physfs_test_utils.h @@ -0,0 +1,9 @@ +#pragma once + +#include +#include + +bool InitPhysFSTest(const std::filesystem::path& writeDir); +bool MountPhysFSTest(const std::filesystem::path& realPath, const std::string& mountPoint); +bool UnmountPhysFSTest(const std::filesystem::path& realPath); +void ShutdownPhysFSTest(); diff --git a/tests/resource_tests.cpp b/tests/resource_tests.cpp new file mode 100644 index 0000000..78aa947 --- /dev/null +++ b/tests/resource_tests.cpp @@ -0,0 +1,292 @@ +#define TEST_NO_MAIN +#include "acutest.h" +#include +#include +#include +#include "../include/ShaderManager.h" +#include "../include/ModelManager.h" +#include "../include/MaterialManager.h" +#include "../include/TextureManager.h" +#include "../include/AssetManager.h" +#include "../include/scripting/ScriptEngine.h" +#include "../include/scripting/ResourceBindings.h" +#include "raylib.h" +#include "physfs_test_utils.h" +extern "C" { +void acutest_skip_(const char* file, int line, const char* fmt, ...); +} +static bool HasDisplay() { +#if defined(_WIN32) + return true; +#elif defined(__APPLE__) + return true; +#else + const char* display = std::getenv("DISPLAY"); + const char* wayland = std::getenv("WAYLAND_DISPLAY"); + return (display && *display) || (wayland && *wayland); +#endif +} + +static bool InitRaylibHidden() { + const char* forceHeadless = std::getenv("SIMIAN_HEADLESS"); + if (forceHeadless && forceHeadless[0] == '1') { + return false; + } + + if (!HasDisplay()) { + return false; + } + + if (!IsWindowReady()) { + SetConfigFlags(FLAG_WINDOW_HIDDEN); + InitWindow(1, 1, "simian_test"); + SetTargetFPS(60); + } + + return IsWindowReady(); +} + +static void ShutdownRaylibHidden() { + if (IsWindowReady()) { + CloseWindow(); + } +} + +static std::filesystem::path WriteTempShader(const std::filesystem::path& dir, const std::string& name, const std::string& code) { + std::filesystem::path path = dir / name; + std::ofstream ofs(path); + ofs << code; + return path; +} + +void test_shader_manager(void) { + if (!InitRaylibHidden()) { + TEST_SKIP("No display available for raylib"); + return; + } + + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_shader"; + std::filesystem::create_directories(tmpDir); + + const std::string vsCode = + "#version 330\n" + "layout(location = 0) in vec3 vertexPosition;\n" + "uniform mat4 mvp;\n" + "void main() {\n" + " gl_Position = mvp * vec4(vertexPosition, 1.0);\n" + "}\n"; + + const std::string fsCode = + "#version 330\n" + "out vec4 fragColor;\n" + "void main() {\n" + " fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n" + "}\n"; + + auto vsPath = WriteTempShader(tmpDir, "test.vs", vsCode); + auto fsPath = WriteTempShader(tmpDir, "test.fs", fsCode); + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + ShaderManager shaderManager; + unsigned int shaderId = shaderManager.Load("test.vs", "test.fs"); + TEST_CHECK(shaderId != 0); + TEST_CHECK(shaderManager.Get(shaderId) != nullptr); + + shaderManager.Unload(shaderId); + TEST_CHECK(shaderManager.Get(shaderId) == nullptr); + + ShutdownRaylibHidden(); + + + std::filesystem::remove(vsPath); + std::filesystem::remove(fsPath); + std::filesystem::remove(tmpDir); +} + +void test_model_manager(void) { + if (!InitRaylibHidden()) { + TEST_SKIP("No display available for raylib"); + return; + } + + ModelManager modelManager; + Mesh mesh = GenMeshCube(1.0f, 1.0f, 1.0f); + unsigned int modelId = modelManager.LoadFromMesh(mesh); + TEST_CHECK(modelId != 0); + TEST_CHECK(modelManager.Get(modelId) != nullptr); + + modelManager.Unload(modelId); + TEST_CHECK(modelManager.Get(modelId) == nullptr); + + ShutdownRaylibHidden(); +} + +void test_material_manager(void) { + MaterialManager materialManager; + Color albedo = {1, 2, 3, 4}; + unsigned int materialId = materialManager.Create(7, albedo); + TEST_CHECK(materialId != 0); + + MaterialData* data = materialManager.Get(materialId); + TEST_CHECK(data != nullptr); + TEST_CHECK(data->shaderId == 7); + TEST_CHECK(data->albedo.r == 1); + TEST_CHECK(data->albedo.g == 2); + TEST_CHECK(data->albedo.b == 3); + TEST_CHECK(data->albedo.a == 4); + + materialManager.SetShader(materialId, 42); + TEST_CHECK(data->shaderId == 42); + + Color newAlbedo = {10, 20, 30, 40}; + materialManager.SetAlbedo(materialId, newAlbedo); + TEST_CHECK(data->albedo.r == 10); + TEST_CHECK(data->albedo.g == 20); + TEST_CHECK(data->albedo.b == 30); + TEST_CHECK(data->albedo.a == 40); + + materialManager.Remove(materialId); + TEST_CHECK(materialManager.Get(materialId) == nullptr); +} + +void test_resource_bindings_script(void) { + if (!InitRaylibHidden()) { + TEST_SKIP("No display available for raylib"); + return; + } + + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_resource"; + std::filesystem::create_directories(tmpDir); + + const std::string vsCode = + "#version 330\n" + "layout(location = 0) in vec3 vertexPosition;\n" + "uniform mat4 mvp;\n" + "void main() {\n" + " gl_Position = mvp * vec4(vertexPosition, 1.0);\n" + "}\n"; + + const std::string fsCode = + "#version 330\n" + "out vec4 fragColor;\n" + "void main() {\n" + " fragColor = vec4(0.0, 1.0, 0.0, 1.0);\n" + "}\n"; + + auto vsPath = WriteTempShader(tmpDir, "script.vs", vsCode); + auto fsPath = WriteTempShader(tmpDir, "script.fs", fsCode); + + std::filesystem::path scriptPath = tmpDir / "resource_test.as"; + { + std::ofstream script(scriptPath); + script << "uint shaderId = 0;\n"; + script << "uint modelId = 0;\n"; + script << "uint materialId = 0;\n"; + script << "void Init() {\n"; + script << " shaderId = Shader::Load(\"script.vs\", \"script.fs\");\n"; + script << " modelId = Model::LoadCube(1.0f, 1.0f, 1.0f);\n"; + script << " materialId = Material::Create(shaderId, 255, 0, 0, 255);\n"; + script << "}\n"; + } + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + { + ShaderManager shaderManager; + ModelManager modelManager; + MaterialManager materialManager; + TextureManager textureManager; + SetResourceManagers(&shaderManager, &modelManager, &materialManager, &textureManager); + + ScriptEngine se; + TEST_CHECK(se.Initialize() == true); + bool compiled = se.CompileScript("resource_test.as"); + TEST_CHECK(compiled == true); + + asIScriptFunction* initFunc = se.GetInitFunction(); + TEST_CHECK(initFunc != nullptr); + se.CallScriptFunction(initFunc); + + asIScriptEngine* engine = se.GetEngine(); + TEST_CHECK(engine != nullptr); + asIScriptModule* module = engine ? engine->GetModule("main") : nullptr; + TEST_CHECK(module != nullptr); + + auto fetchUInt = [module](const char* name) -> unsigned int { + int idx = module->GetGlobalVarIndexByName(name); + TEST_CHECK_(idx >= 0, "Global '%s' not found", name); + if (idx < 0) return 0; + void* address = module->GetAddressOfGlobalVar(idx); + TEST_CHECK_(address != nullptr, "Global '%s' address null", name); + if (!address) return 0; + return *static_cast(address); + }; + + unsigned int shaderId = fetchUInt("shaderId"); + unsigned int modelId = fetchUInt("modelId"); + unsigned int materialId = fetchUInt("materialId"); + + TEST_CHECK(shaderId != 0); + TEST_CHECK(modelId != 0); + TEST_CHECK(materialId != 0); + + se.Shutdown(); + } + + std::filesystem::remove(scriptPath); + std::filesystem::remove(vsPath); + std::filesystem::remove(fsPath); + std::filesystem::remove(tmpDir); + + ShutdownRaylibHidden(); + +} + +void test_asset_registry_material(void) { + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_asset_registry"; + std::filesystem::create_directories(tmpDir); + std::filesystem::path registryPath = tmpDir / "registry.toml"; + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + MaterialManager materialManager; + AssetManager assets; + assets.SetManagers(nullptr, nullptr, &materialManager, nullptr); + + Color albedo = {10, 20, 30, 40}; + unsigned int materialId = assets.CreateMaterial("test_mat", 0, albedo); + TEST_CHECK(materialId != 0); + + bool saved = assets.SaveRegistry("registry.toml"); + TEST_CHECK(saved == true); + + assets.Clear(); + materialManager.Clear(); + + MaterialManager materialManagerReload; + AssetManager assetsReload; + assetsReload.SetManagers(nullptr, nullptr, &materialManagerReload, nullptr); + + bool loaded = assetsReload.LoadRegistry("registry.toml"); + TEST_CHECK(loaded == true); + + unsigned int loadedId = assetsReload.GetMaterialId("test_mat"); + TEST_CHECK(loadedId != 0); + MaterialData* data = materialManagerReload.Get(loadedId); + TEST_CHECK(data != nullptr); + if (data) { + TEST_CHECK(data->albedo.r == 10); + TEST_CHECK(data->albedo.g == 20); + TEST_CHECK(data->albedo.b == 30); + TEST_CHECK(data->albedo.a == 40); + } + + ShutdownPhysFSTest(); + + std::filesystem::remove(registryPath); + std::filesystem::remove(tmpDir); +} diff --git a/tests/resource_tests.h b/tests/resource_tests.h new file mode 100644 index 0000000..f2f9ed4 --- /dev/null +++ b/tests/resource_tests.h @@ -0,0 +1,7 @@ +#pragma once + +void test_shader_manager(void); +void test_model_manager(void); +void test_material_manager(void); +void test_resource_bindings_script(void); +void test_asset_registry_material(void); diff --git a/tests/scenegraph_tests.cpp b/tests/scenegraph_tests.cpp new file mode 100644 index 0000000..0bfbb2d --- /dev/null +++ b/tests/scenegraph_tests.cpp @@ -0,0 +1,369 @@ +#define TEST_NO_MAIN +#include "acutest.h" +#include +#include +#include +#include "../include/ECSComponents.h" +#include "../include/SceneGraph.h" +#include "../include/SceneLoader.h" +#include "../include/ModelManager.h" +#include "../include/ShaderManager.h" +#include "../include/TextureManager.h" +#include "../include/AssetManager.h" +#include "raylib.h" +#include "entt.hpp" +#include "physfs_test_utils.h" + +extern "C" { +void acutest_skip_(const char* file, int line, const char* fmt, ...); +} +static bool HasDisplay() { +#if defined(_WIN32) + return true; +#elif defined(__APPLE__) + return true; +#else + const char* display = std::getenv("DISPLAY"); + const char* wayland = std::getenv("WAYLAND_DISPLAY"); + return (display && *display) || (wayland && *wayland); +#endif +} + +static bool InitRaylibHidden() { + const char* forceHeadless = std::getenv("SIMIAN_HEADLESS"); + if (forceHeadless && forceHeadless[0] == '1') { + return false; + } + + if (!HasDisplay()) { + return false; + } + + if (!IsWindowReady()) { + SetConfigFlags(FLAG_WINDOW_HIDDEN); + InitWindow(1, 1, "simian_test_scene"); + SetTargetFPS(60); + } + + return IsWindowReady(); +} + +static void ShutdownRaylibHidden() { + if (IsWindowReady()) { + CloseWindow(); + } +} + +void test_scenegraph_world_transform(void) { + entt::registry registry; + + entt::entity parent = registry.create(); + entt::entity child = registry.create(); + + registry.emplace(parent, Vector3{1.0f, 0.0f, 0.0f}, Vector3{2.0f, 2.0f, 2.0f}, 0.0f); + registry.emplace(child, Vector3{1.0f, 0.0f, 0.0f}, Vector3{1.0f, 1.0f, 1.0f}, 0.0f); + + AttachChild(registry, child, parent); + UpdateWorldTransforms(registry); + + auto* childWorld = registry.try_get(child); + TEST_CHECK(childWorld != nullptr); + if (childWorld) { + TEST_CHECK(childWorld->position.x == 3.0f); + TEST_CHECK(childWorld->position.y == 0.0f); + TEST_CHECK(childWorld->position.z == 0.0f); + } +} + +void test_scenegraph_detach(void) { + entt::registry registry; + + entt::entity parent = registry.create(); + entt::entity child = registry.create(); + + AttachChild(registry, child, parent); + DetachFromParent(registry, child); + + auto* childHierarchy = registry.try_get(child); + TEST_CHECK(childHierarchy != nullptr); + if (childHierarchy) { + TEST_CHECK(childHierarchy->parent == entt::null); + TEST_CHECK(childHierarchy->prev == entt::null); + TEST_CHECK(childHierarchy->next == entt::null); + } + + auto* parentHierarchy = registry.try_get(parent); + TEST_CHECK(parentHierarchy != nullptr); + if (parentHierarchy) { + TEST_CHECK(parentHierarchy->first == entt::null); + } +} + +void test_scene_loader_basic(void) { + if (!InitRaylibHidden()) { + TEST_SKIP("No display available for raylib"); + return; + } + + entt::registry registry; + + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene"; + std::filesystem::create_directories(tmpDir); + std::filesystem::path scenePath = tmpDir / "scene.toml"; + + { + std::ofstream scene(scenePath); + scene << "[[entity]]\n"; + scene << "id = \"root\"\n"; + scene << "tag = \"Root\"\n"; + scene << "position = [0.0, 0.0, 0.0]\n"; + scene << "\n"; + scene << "[[entity]]\n"; + scene << "id = \"parent\"\n"; + scene << "parent = \"root\"\n"; + scene << "tag = \"Parent\"\n"; + scene << "position = [1.0, 0.0, 0.0]\n"; + scene << "model = \"cube\"\n"; + scene << "model_size = [1.0, 1.0, 1.0]\n"; + scene << "color = 0x00FF00FF\n"; + scene << "\n"; + scene << "[[entity]]\n"; + scene << "id = \"child\"\n"; + scene << "parent = \"parent\"\n"; + scene << "tag = \"Child\"\n"; + scene << "position = [0.0, 1.0, 0.0]\n"; + scene << "model = \"sphere\"\n"; + scene << "radius = 0.5\n"; + scene << "color = 0xFF0000FF\n"; + } + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + { + ModelManager modelManager; + ShaderManager shaderManager; + TextureManager textureManager; + SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager); + + entt::entity root = LoadSceneFromFile("scene.toml", true); + TEST_CHECK(root != entt::null); + } + + entt::entity parentEntity = entt::null; + entt::entity childEntity = entt::null; + + auto tagView = registry.view(); + for (auto entity : tagView) { + const auto& tag = tagView.get(entity); + if (tag.name == "Parent") { + parentEntity = entity; + } else if (tag.name == "Child") { + childEntity = entity; + } + } + + TEST_CHECK(parentEntity != entt::null); + TEST_CHECK(childEntity != entt::null); + + if (parentEntity != entt::null && childEntity != entt::null) { + auto* childHierarchy = registry.try_get(childEntity); + TEST_CHECK(childHierarchy != nullptr); + if (childHierarchy) { + TEST_CHECK(childHierarchy->parent == parentEntity); + } + TEST_CHECK(registry.all_of(parentEntity)); + TEST_CHECK(registry.all_of(childEntity)); + } + + ShutdownRaylibHidden(); + + + std::filesystem::remove(scenePath); + std::filesystem::remove(tmpDir); +} + +void test_scene_loader_scene_script_path(void) { + entt::registry registry; + ModelManager modelManager; + ShaderManager shaderManager; + TextureManager textureManager; + SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager); + + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene_script"; + std::filesystem::create_directories(tmpDir); + std::filesystem::path scenePath = tmpDir / "scene.toml"; + + { + std::ofstream scene(scenePath); + scene << "scene_script = \"scripts/demo_scene.as\"\n"; + scene << "\n"; + scene << "[[entity]]\n"; + scene << "id = \"root\"\n"; + scene << "tag = \"Root\"\n"; + } + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + entt::entity root = LoadSceneFromFile("scene.toml", true); + TEST_CHECK(root != entt::null); + + std::string sceneScript; + bool changed = ConsumeSceneScriptPath(sceneScript); + TEST_CHECK(changed == true); + TEST_CHECK(sceneScript == "scripts/demo_scene.as"); + + + std::filesystem::remove(scenePath); + std::filesystem::remove(tmpDir); +} + +void test_scene_loader_clear_scene_script(void) { + entt::registry registry; + ModelManager modelManager; + ShaderManager shaderManager; + TextureManager textureManager; + SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager); + + SetSceneScriptPath("scripts/old_scene.as"); + std::string previous; + ConsumeSceneScriptPath(previous); + + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene_clear"; + std::filesystem::create_directories(tmpDir); + std::filesystem::path scenePath = tmpDir / "scene.toml"; + + { + std::ofstream scene(scenePath); + scene << "[[entity]]\n"; + scene << "id = \"root\"\n"; + scene << "tag = \"Root\"\n"; + } + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + LoadSceneFromFile("scene.toml", true); + + std::string sceneScript; + bool changed = ConsumeSceneScriptPath(sceneScript); + TEST_CHECK(changed == true); + TEST_CHECK(sceneScript.empty()); + + ShutdownPhysFSTest(); + + std::filesystem::remove(scenePath); + std::filesystem::remove(tmpDir); +} + +void test_scene_loader_asset_keys(void) { + if (!InitRaylibHidden()) { + TEST_SKIP("No display available for raylib"); + return; + } + + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene_assets"; + std::filesystem::create_directories(tmpDir); + std::filesystem::path registryPath = tmpDir / "registry.toml"; + std::filesystem::path scenePath = tmpDir / "scene_asset.toml"; + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + { + ModelManager modelManager; + ShaderManager shaderManager; + TextureManager textureManager; + AssetManager assets; + assets.SetManagers(&modelManager, &shaderManager, nullptr, &textureManager); + assets.LoadCube("demo_cube", 1.0f, 1.0f, 1.0f); + assets.SaveRegistry("registry.toml"); + } + + entt::registry registry; + { + ModelManager modelManager; + ShaderManager shaderManager; + TextureManager textureManager; + AssetManager assets; + assets.SetManagers(&modelManager, &shaderManager, nullptr, &textureManager); + assets.LoadRegistry("registry.toml"); + + SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager); + SetSceneAssetManager(&assets); + + { + std::ofstream scene(scenePath); + scene << "[[entity]]\n"; + scene << "id = \"root\"\n"; + scene << "tag = \"Root\"\n"; + scene << "position = [0.0, 0.0, 0.0]\n"; + scene << "model_asset = \"demo_cube\"\n"; + scene << "color = 0xFFFFFFFF\n"; + scene << "\n"; + } + + entt::entity root = LoadSceneFromFile("scene_asset.toml", true); + TEST_CHECK(root != entt::null); + + auto tagView = registry.view(); + entt::entity rootEntity = entt::null; + for (auto entity : tagView) { + const auto& tag = tagView.get(entity); + if (tag.name == "Root") { + rootEntity = entity; + break; + } + } + + TEST_CHECK(rootEntity != entt::null); + if (rootEntity != entt::null) { + auto* renderer = registry.try_get(rootEntity); + TEST_CHECK(renderer != nullptr); + if (renderer) { + unsigned int expectedId = assets.GetModelId("demo_cube"); + TEST_CHECK(expectedId != 0); + TEST_CHECK(renderer->modelId == expectedId); + } + } + } + + ShutdownRaylibHidden(); + + ShutdownPhysFSTest(); + + std::filesystem::remove(scenePath); + std::filesystem::remove(registryPath); + std::filesystem::remove(tmpDir); +} + +void test_scene_loader_rejects_escape(void) { + entt::registry registry; + ModelManager modelManager; + ShaderManager shaderManager; + TextureManager textureManager; + SetSceneContext(®istry, &modelManager, &shaderManager, &textureManager); + + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_scene_sandbox"; + std::filesystem::create_directories(tmpDir); + std::filesystem::path outsidePath = std::filesystem::current_path() / "outside_scene.toml"; + + { + std::ofstream scene(outsidePath); + scene << "[[entity]]\n"; + scene << "id = \"root\"\n"; + scene << "tag = \"Root\"\n"; + } + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + entt::entity root = LoadSceneFromFile("../outside_scene.toml", true); + TEST_CHECK(root == entt::null); + + ShutdownPhysFSTest(); + + std::filesystem::remove(outsidePath); + std::filesystem::remove(tmpDir); +} diff --git a/tests/scenegraph_tests.h b/tests/scenegraph_tests.h new file mode 100644 index 0000000..bf404b9 --- /dev/null +++ b/tests/scenegraph_tests.h @@ -0,0 +1,9 @@ +#pragma once + +void test_scenegraph_world_transform(void); +void test_scenegraph_detach(void); +void test_scene_loader_basic(void); +void test_scene_loader_asset_keys(void); +void test_scene_loader_scene_script_path(void); +void test_scene_loader_clear_scene_script(void); +void test_scene_loader_rejects_escape(void); diff --git a/tests/script_engine_tests.cpp b/tests/script_engine_tests.cpp new file mode 100644 index 0000000..776c9e1 --- /dev/null +++ b/tests/script_engine_tests.cpp @@ -0,0 +1,69 @@ +#define TEST_NO_MAIN +#include "acutest.h" +#include +#include +#include "../include/scripting/ScriptEngine.h" +#include "physfs_test_utils.h" + +void test_scriptengine_compile_and_call(void) { + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_script"; + std::filesystem::create_directories(tmpDir); + std::filesystem::path script = tmpDir / "simple.as"; + { + std::ofstream ofs(script); + ofs << "float g_x = 0;\n"; + ofs << "void Update(float dt) { g_x += dt * 10.0f; }\n"; + } + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + ScriptEngine se; + TEST_CHECK(se.Initialize() == true); + bool ok = se.CompileScript("simple.as"); + TEST_CHECK(ok == true); + asIScriptFunction* upd = se.GetUpdateFunction(); + TEST_CHECK(upd != nullptr); + + se.CallScriptFunction(upd, 0.1f); + + se.Shutdown(); + + ShutdownPhysFSTest(); + + std::filesystem::remove(script); + std::filesystem::remove(tmpDir); +} + +void test_scriptengine_include(void) { + std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_include"; + std::filesystem::create_directories(tmpDir); + + std::filesystem::path inc = tmpDir / "inc.as"; + { + std::ofstream ofsinc(inc); + ofsinc << "void Included() { Print(\"Included called\"); }\n"; + } + + std::filesystem::path main = tmpDir / "main.as"; + { + std::ofstream ofsmain(main); + ofsmain << "#include \"inc.as\"\n"; + ofsmain << "void Update(float dt) { Included(); }\n"; + } + + TEST_CHECK(InitPhysFSTest(tmpDir) == true); + TEST_CHECK(MountPhysFSTest(tmpDir, "") == true); + + ScriptEngine se; + TEST_CHECK(se.Initialize() == true); + bool ok = se.CompileScript("main.as"); + TEST_CHECK(ok == true); + se.Shutdown(); + + ShutdownPhysFSTest(); + + std::filesystem::remove(inc); + std::filesystem::remove(main); + std::filesystem::remove(tmpDir); +} diff --git a/tests/script_engine_tests.h b/tests/script_engine_tests.h new file mode 100644 index 0000000..641f395 --- /dev/null +++ b/tests/script_engine_tests.h @@ -0,0 +1,4 @@ +#pragma once + +void test_scriptengine_compile_and_call(void); +void test_scriptengine_include(void); diff --git a/tests/test.cpp b/tests/test.cpp new file mode 100644 index 0000000..1cffdd5 --- /dev/null +++ b/tests/test.cpp @@ -0,0 +1,25 @@ +#include "acutest.h" +#include "hot_reload.h" +#include "script_engine_tests.h" +#include "math_bindings_tests.h" +#include "resource_tests.h" +#include "scenegraph_tests.h" +TEST_LIST = { + { "hotreload_dir", test_hotreload_directory_watch }, + { "script_compile", test_scriptengine_compile_and_call }, + { "script_include", test_scriptengine_include }, + { "math_bindings", test_math_bindings }, + { "shader_manager", test_shader_manager }, + { "model_manager", test_model_manager }, + { "material_manager", test_material_manager }, + { "asset_registry_material", test_asset_registry_material }, + { "resource_bindings", test_resource_bindings_script }, + { "scenegraph_world", test_scenegraph_world_transform }, + { "scenegraph_detach", test_scenegraph_detach }, + { "scene_loader", test_scene_loader_basic }, + { "scene_loader_script_path", test_scene_loader_scene_script_path }, + { "scene_loader_clear_script", test_scene_loader_clear_scene_script }, + { "scene_loader_assets", test_scene_loader_asset_keys }, + { "scene_loader_sandbox", test_scene_loader_rejects_escape }, + { NULL, NULL } +}; diff --git a/tests/unit_tests.cpp b/tests/unit_tests.cpp deleted file mode 100644 index b211542..0000000 --- a/tests/unit_tests.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "../external/acutest/include/acutest.h" -#include -#include -#include -#include -#include -#include "../include/HotReload.h" -#include "../include/ScriptEngine.h" - -using namespace std::chrono_literals; - -/* simple sanity test */ -void test_one(void) { - TEST_CHECK(1 + 1 == 2); -} - -void test_str(void) { - TEST_CHECK(strcmp("foo","foo") == 0); -} - -void test_hotreload_directory_watch(void) { - std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_watch"; - std::filesystem::create_directories(tmpDir); - - std::filesystem::path f = tmpDir / "a.as"; - std::ofstream ofs(f); - ofs << "// test" << std::endl; - ofs.close(); - - HotReload hr(tmpDir.string()); - TEST_CHECK(hr.CheckForChanges() == false); - - std::this_thread::sleep_for(1s); - std::ofstream ofs2(f, std::ios::app); - ofs2 << "// changed" << std::endl; - ofs2.close(); - - TEST_CHECK(hr.CheckForChanges() == true); - - std::filesystem::remove(f); - std::filesystem::remove(tmpDir); -} - -void test_scriptengine_compile_and_call(void) { - // Create a simple script file that defines Update(float) and a global - std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_script"; - std::filesystem::create_directories(tmpDir); - std::filesystem::path script = tmpDir / "simple.as"; - std::ofstream ofs(script); - ofs << "float g_x = 0;\n"; - ofs << "void Update(float dt) { g_x += dt * 10.0f; }\n"; - ofs.close(); - - ScriptEngine se; - TEST_CHECK(se.Initialize() == true); - bool ok = se.CompileScript(script.string()); - TEST_CHECK(ok == true); - asIScriptFunction* upd = se.GetUpdateFunction(); - TEST_CHECK(upd != nullptr); - - // Call Update with dt=0.1 - se.CallScriptFunction(upd, 0.1f); - - se.Shutdown(); - - std::filesystem::remove(script); - std::filesystem::remove(tmpDir); -} - -void test_scriptengine_include(void) { - std::filesystem::path tmpDir = std::filesystem::current_path() / "tmp_include"; - std::filesystem::create_directories(tmpDir); - std::filesystem::path inc = tmpDir / "inc.as"; - std::ofstream ofsinc(inc); - ofsinc << "void Included() { Print(\"Included called\"); }\n"; - ofsinc.close(); - - std::filesystem::path main = tmpDir / "main.as"; - std::ofstream ofsmain(main); - ofsmain << "#include \"inc.as\"\n"; - ofsmain << "void Update(float dt) { Included(); }\n"; - ofsmain.close(); - - ScriptEngine se; - TEST_CHECK(se.Initialize() == true); - // Compile should succeed and include callback should resolve inc.as - bool ok = se.CompileScript(main.string()); - TEST_CHECK(ok == true); - se.Shutdown(); - - std::filesystem::remove(inc); - std::filesystem::remove(main); - std::filesystem::remove(tmpDir); -} - -TEST_LIST = { - { "one", test_one }, - { "str", test_str }, - { "hotreload_dir", test_hotreload_directory_watch }, - { "script_compile", test_scriptengine_compile_and_call }, - { "script_include", test_scriptengine_include }, - { NULL, NULL } -};