From 236e7c0433b44a0a75540608b5aec615300ba501 Mon Sep 17 00:00:00 2001 From: zhenyan121 <104683324+zhenyan121@users.noreply.github.com> Date: Fri, 7 Aug 2026 10:19:49 +0800 Subject: [PATCH] feature: creature (#38) * build(deps): add assimp library as dependency * feat(render): add model loading and rendering pipeline * refactor(render): rename depth player shaders to depth model and clean up * build: add EnTT library * feat(entity): add ECS-based entity rendering * feat(render): add shadow pass for entity models * refactor(render): unify model and player rendering with single shader pipeline * refactor(collision): convert AABB to center-half representation and migrate player data to ECS * refactor(gameplay): add base classes Chunk and World for shared logic * feat(gameplay): implement entity movement system with collision detection - Add Velocity and HitBoxes components to entity - Introduce HitboxManager for loading per-entity collision AABBs from JSON - Create MoveSystem with per-axis collision handling - Move get_block_aabb to base World class and add virtual get_per_tick_time - Remove static get_block_aabb from ClientWorld; use member m_per_tick_time for tick duration * fix(gameplay): correct hitbox insertion in HitboxManager::load * build: remove entt library * refactor(gameplay): replace entt components with SparseVector for player data Refactor client world to use a custom SparseVector for player data instead of entt registry. Consolidate Transform, ViewAngles, and related structs into Position, Orientation, WalkPose. Introduce PlayerData and PlayerRenderData. Remove the unused move_system.cpp. Unify player render and shadow render into a single function. * feat(render): add model ID system with concurrent lookup and namespace-based loading - Extract `ModelID` and `Model` struct to new `model.hpp` - Replace `std::unordered_map` with `tbb::concurrent_hash_map` for thread safety - Add `get_model(ModelID)`, `get_model_id`, `get_model_name` methods - Parse model names in `namespace:name` format to construct asset paths - Update `load_model` to accept `string_view` and use ID-based management * refactor(gameplay): extract movement, gravity, orientation, and walk pose into structs * refactor(gameplay): extract Entity base class from ClientPlayer Move position, walk pose, velocity, orientation, movement, and gravity fields and their accessors into a new Entity base class. ClientPlayer now inherits from Entity, removing duplicated members. Also update velocity handling to use 3D vector per axis and adjust related logic. * feat(player): split max speed into horizontal/vertical, set spawn pos * refactor: move movement logic into SpeedSystem and Entity components * refactor(gameplay): extract physics and collision from ClientPlayer into PhysicalSystem Move per-axis collision detection and move distance calculation to new PhysicalSystem. Move SpeedSystem implementation from inline header to separate .cpp file. Add const accessors to Entity. Replace inline AABB helper with HitboxManager registration of player hitbox using new PLAYER_SIZE constant. Remove obsolete members and functions from ClientPlayer. * refactor(gameplay): integrate model and hitbox ID system into Entity - Introduce HitboxID, EntityID, and ModelID types for safer ID-based lookups - Replace AABB struct with Hitbox (includes HitboxID) - Convert ModelManager and HitboxManager to singletons with Handle structs - Update Entity to store IDs for model and hitbox, removing direct references - Reorganize creature model assets into subdirectories per entity type - Add player model (player.glb) and collision data for pig - Remove ModelManager dependency from App and Renderer - Add namespace parsing utility for asset paths - Mark sparse_vector::insert() with [[nodiscard]] * build: add entt library * refactor(gameplay): rename ClientPlayer to LocalPlayer * refactor(gameplay): separate player logic into manager and ECS components * feat(gameplay): add entity managers and refactor to ECS components * refactor(gameplay): replace client thread with timer-based system and add entity manager * feat(gameplay): implement entity system with concurrent task handling and fix model loading * feat(gameplay): add entity destruction support * feat(entity): add client-to-server entity creation and destruction requests Extend the network protocol with `C2SEntityCreateRequest` and `C2SEntityDestoryRequest` packets. Refactor entity managers to split public client-facing create/destroy methods that send requests over the network, from internal handlers that process received packets. Add utility functions for converting protobuf Vec3 to glm::vec3. * feat(gameplay): implement entity update packets Add S2CEntityUpdate to sync entity positions from server to client, including update handling in the client entity manager and server-side AI updates. * feat(ai): add wander AI system with move boost Add AIBase and WanderAITag components, a WanderAISystem, and a MoveBoost component to control wandering behavior. Replace the old MoveState with MoveBoost and include a horizontal random direction helper. * refactor(ecs): move entity physics to tick-based systems Update PhysicalSystem and SpeedSystem to operate on entt::registry instead of individual components. Add TickVelocity for server creatures so movement is calculated per tick without frame delta time. LocalPlayer now implements its own client-side physics with collision detection. * feat(gameplay): add server-side entity movement and AI Refactor LocalPlayer::update_physical to operate on a passed position, and run speed, physical, and wander AI systems in ServerEntityManager. * feat(gameplay): add pig wander AI and refine speed physics * feat(server): send entity position updates to all sessions * fix: correct velocity clamping and hitbox loading Preserve sign when clamping velocity; negative velocities now clamp to zero instead of flipping direction. Support loading hitbox definitions from JSON arrays and objects. Tune pig entity movement, spawn height, and wander probabilities. * feat(creatures): refine pig movement physics Add movement constants for pigs and switch deceleration to per-axis friction so creatures stop naturally. Extend wander boost duration for smoother behavior. * feat(physics): implement step-up for horizontal collisions * feat: sync entity direction and rotate models accordingly Move direction into the transform component and include it in server-to-client entity updates. Client now sets transform direction from the network message and uses it to compute yaw for model rotation, so entities visually face their movement direction. Refactor net_utils to support arbitrary Vec3 fields. * feat(render): interpolate entity transforms for rendering Add RenderTransform component to smooth position/direction updates and use it in entity and shadow passes. * fix(server_world): send time before entity updates * build: replace nlohmann json with rapidjson * refactor(json): migrate from nlohmann to rapidjson Replace nlohmann::json with rapidjson across localization, hitbox manager, server world, and sensitive filter. Add json_utils helper for converting rapidjson documents to maps, and improve parse error handling. * feat(item): add item manager with JSON asset loading Implement ItemManager to load and query item definitions from assets/item JSON files. Add ItemData struct, item asset metadata, CMake source registration, and initialize the manager during app startup. Also add AGENTS.md repository guidelines. * refactor(item): decouple items from block types The item system now supports an ItemKind and property, allowing item JSON to declare a type instead of assuming every item is a block. BlockManager was moved to its own header, redesigned around concurrent hash maps, and exposes id_from_name(). Texture and UI code now key items by ItemID, while block placement resolves the block type from the item registry. * fix(gameplay): use inline const for static EMPTY members Use inline const for static EMPTY members to avoid ODR and linker issues. Add missing block_manager.hpp includes in gameplay sources. * fix: stabilize block item registration and display Store item names as owned strings in ItemManager, set block type property for block items, validate item kind before placement, and handle items without textures gracefully in inventory UI. * feat: add pig spawn egg item Add pig spawn egg asset and texture, parse spawn egg item kind and creature property, and spawn the configured entity when used on an empty block. Also fix item texture loading to use actual image dimensions and update selected item UI sizing. * refactor(gameplay): update systems per entity with chunk check Refactor server entity update flow to process entities individually, skipping those whose chunk is not loaded. System update methods now accept a single entity instead of iterating the full registry view, and `ServerWorld::get_chunk_ref_count` is added to determine if an entity's chunk is active. * perf(render): skip entities outside loaded chunks * feat: add --direct-enter option to skip to world scene Support a new CLI flag that launches the app directly into the world scene. If no IP is provided, it starts a local server on the specified port and connects to 127.0.0.1; otherwise it connects to the given IP. With this flag enabled, a port must be specified via -p. Also expose SceneManager::push as public so scenes can be pushed immediately when bypassing the normal menu flow. * refactor(localization): switch block translations to item naming Remove the `name_key` field from block definitions and use item-based localization keys (`item.*.name`) for inventory display. Item data now stores the localized name at load time. * feat(gameplay): spawn creatures during chunk generation Add creature spawning to chunk generation with a configurable SpawnConfig, including a default pig spawn. Spawning occurs in the final generation phase and registers entities through the server world's entity manager. Also destroy entities when their chunk is unloaded to prevent orphaned entities and expose the entity manager from ServerWorld. * feat(gameplay): add run mode based thread pool config Introduce RunMode enum and thread pool sizing helpers for client, server, and hybrid modes. Add compute pool to server world and pass mode through server/client initialization. * perf(server): parallelize entity update loop Use parallel_do to process entities concurrently via the compute pool. Switch get_all_session to a thread-safe tbb::concurrent_vector and make ChunkEntity's ref_count atomic to avoid data races. Also remove the debug pig spawn from world init. * feat(creatures): animate creature walk cycles Add a Gait component synchronized over the network and procedural animation for model nodes. Load animation parameters from assets/model/creature/pig/animation.json and apply leg swing, body bob, and head motion based on gait. Refactor Gait into its own header for reuse. * fix(gameplay): make entity interpolation frame-rate independent * perf(render): batch model rendering with instancing Replaces per-entity draw calls with instanced rendering for both main and shadow passes. Each model's node hierarchy is flattened once and instance matrices are updated per frame, reducing draw calls and CPU overhead. * refactor(render): build entity instance buffers once per frame Move instance matrix upload into build_vertices and use a precomputed instance data map for both shadow and color passes, avoiding duplicate GPU buffer updates. * perf(gameplay): batch entity updates into single packet Aggregate per-entity update messages into S2CEntityUpdateBatch and broadcast once per tick. Reuse serialized packets in several broadcast loops to avoid repeated make_packet calls. * fix: correct thread count and shadow projection uniform - Add reserved client thread count to client threads in server generation calculation. - Set projection matrix uniform only for non-shadow rendering. * feat(ecs): add entity type separation and creature limits Replace generic entity creation with typed creatures and items. Rename `add_entity` to `add_creature`, enforce per-player creature cap, and track entity/creature totals for metrics. Expose run mode to dev panel and show new counts. * perf(render): frustum cull entities before instance building Add frustum culling to entity instance data collection using AABB vs camera frustum planes. Pass renderer into get_instances_data_map to compute MVP and extract planes. Also add "Rendered Entities" debug counter for visibility. * feat(audio): add ambient pig sounds Add pig call audio with randomized timing, triggered by proximity to the player as 3D positional sound. * feat(client-entity): use snapshot history for entity interpolation Replace exponential smoothing with a buffered snapshot system. Each entity keeps a deque of position/direction snapshots with timestamps; the render transform is interpolated at a fixed 100 ms render delay to hide network jitter. Snapshots are capped to 16 entries. * refactor(hitbox): move player hitbox definition to JSON asset * feat(client_player): use snapshot interpolation for remote players Replace exponential position smoothing with snapshot interpolation. Remote player transforms are rendered from a delayed snapshot history, using linear interpolation for position and shortest-path interpolation for yaw/pitch. A 100 ms render delay compensates for network tick rate. --- AGENTS.md | 251 + CMakeLists.txt | 1 + assets/data/block/air.toml | 1 - assets/data/block/dirt.toml | 1 - assets/data/block/grass.toml | 1 - assets/data/block/grass_block.toml | 1 - assets/data/block/leaf.toml | 1 - assets/data/block/log.toml | 1 - assets/data/block/sand.toml | 1 - assets/data/block/snowy_grass_block.toml | 1 - assets/data/block/stone.toml | 1 - assets/data/block/template.toml | 1 - assets/data/block/water.toml | 1 - assets/item/air.json | 6 + assets/item/dirt.json | 7 + assets/item/grass.json | 7 + assets/item/grass_block.json | 7 + assets/item/leaf.json | 7 + assets/item/log.json | 7 + assets/item/pig_spawn_egg.json | 8 + assets/item/sand.json | 7 + assets/item/snowy_grass_block.json | 7 + assets/item/stone.json | 7 + assets/item/water.json | 7 + assets/lang/en_US.json | 21 +- assets/lang/zh_CN.json | 21 +- assets/model/creature/pig/animation.json | 33 + assets/model/creature/pig/collision.json | 16 + assets/model/creature/pig/pig.glb | Bin 0 -> 15992 bytes assets/model/creature/player/collision.json | 16 + assets/model/creature/player/player.glb | Bin 0 -> 15992 bytes ...ment_shader.glsl => depth_model_frag.glsl} | 0 assets/shaders/depth_model_instance_vert.glsl | 13 + ...shader.glsl => depth_model_vert copy.glsl} | 1 - assets/shaders/depth_model_vert.glsl | 14 + .../{player_f_shader.glsl => model_frag.glsl} | 0 assets/shaders/model_instance_vert.glsl | 26 + .../{player_v_shader.glsl => model_vert.glsl} | 1 - assets/sound/creature/pig/call.mp3 | Bin 0 -> 3779 bytes .../texture/item/spawn_egg/pig_spawn_egg.png | Bin 0 -> 174 bytes cmake/Dependencies.cmake | 2 +- include/Cubed/AABB.hpp | 20 - include/Cubed/app.hpp | 1 - include/Cubed/argument.hpp | 1 + include/Cubed/camera.hpp | 8 +- include/Cubed/dev_panel.hpp | 4 +- include/Cubed/gameplay/block.hpp | 41 +- include/Cubed/gameplay/block_manager.hpp | 45 + include/Cubed/gameplay/chunk.hpp | 27 + include/Cubed/gameplay/chunk_generator.hpp | 2 + include/Cubed/gameplay/client_chunk.hpp | 14 +- .../Cubed/gameplay/client_entity_manager.hpp | 82 + include/Cubed/gameplay/client_player.hpp | 193 +- .../Cubed/gameplay/client_player_manager.hpp | 45 + include/Cubed/gameplay/client_world.hpp | 86 +- include/Cubed/gameplay/creatures/pig.hpp | 13 + include/Cubed/gameplay/creatures/spawn.hpp | 20 + include/Cubed/gameplay/ecs/ai_struct.hpp | 17 + include/Cubed/gameplay/ecs/animation.hpp | 14 + include/Cubed/gameplay/ecs/client_entity.hpp | 31 + include/Cubed/gameplay/ecs/entity.hpp | 14 + include/Cubed/gameplay/ecs/health.hpp | 10 + include/Cubed/gameplay/ecs/identity.hpp | 10 + include/Cubed/gameplay/ecs/movement.hpp | 34 + include/Cubed/gameplay/ecs/server_entity.hpp | 22 + include/Cubed/gameplay/ecs/state.hpp | 18 + include/Cubed/gameplay/ecs/transform.hpp | 30 + .../Cubed/gameplay/{player.hpp => gait.hpp} | 4 +- include/Cubed/gameplay/hitbox.hpp | 26 + include/Cubed/gameplay/hitbox_manager.hpp | 37 + include/Cubed/gameplay/item.hpp | 39 + include/Cubed/gameplay/item_manager.hpp | 39 + include/Cubed/gameplay/item_stack.hpp | 5 +- include/Cubed/gameplay/local_player.hpp | 161 + include/Cubed/gameplay/model.hpp | 7 + include/Cubed/gameplay/network_server.hpp | 3 +- include/Cubed/gameplay/packet.hpp | 34 + include/Cubed/gameplay/server_chunk.hpp | 3 +- .../Cubed/gameplay/server_entity_manager.hpp | 80 + include/Cubed/gameplay/server_player.hpp | 2 +- include/Cubed/gameplay/server_world.hpp | 59 +- .../gameplay/systems/physical_system.hpp | 16 + .../Cubed/gameplay/systems/speed_system.hpp | 11 + .../gameplay/systems/wander_ai_system.hpp | 14 + include/Cubed/gameplay/world.hpp | 32 + include/Cubed/input/input.hpp | 9 - include/Cubed/render/model_manager.hpp | 46 + include/Cubed/render/model_node.hpp | 79 + include/Cubed/render/model_renderer.hpp | 55 + include/Cubed/render/player_renderer.hpp | 4 +- include/Cubed/render/renderer.hpp | 6 +- include/Cubed/render/texture.hpp | 4 +- include/Cubed/render/vertex_array.hpp | 1 + include/Cubed/render/vertex_buffer.hpp | 2 + include/Cubed/render/world_renderer.hpp | 13 +- include/Cubed/scene/scene_manager.hpp | 3 +- include/Cubed/scene/world_scene.hpp | 3 + include/Cubed/texture_manager.hpp | 44 +- include/Cubed/tools/cubed_concepts.hpp | 7 + include/Cubed/tools/cubed_random.hpp | 4 + include/Cubed/tools/json_utils.hpp | 8 + include/Cubed/tools/model_loader.hpp | 22 + include/Cubed/tools/name_space.hpp | 19 + include/Cubed/tools/net_utils.hpp | 24 + include/Cubed/tools/sensitive_filter.hpp | 6 +- include/Cubed/tools/shader_tools.hpp | 2 +- include/Cubed/tools/sparse_vector.hpp | 318 + include/Cubed/tools/threas_utils.hpp | 130 + include/Cubed/tools/time_tools.hpp | 1 + include/Cubed/ui/inventory_ui.hpp | 2 +- include/Cubed/ui/item_slot.hpp | 8 +- include/{nlohmann => entt}/.clang-format | 0 include/entt/config/config.h | 134 + include/entt/config/macro.h | 11 + include/entt/config/version.h | 18 + include/entt/container/dense_map.hpp | 1026 + include/entt/container/dense_set.hpp | 890 + include/entt/container/fwd.hpp | 38 + include/entt/container/table.hpp | 434 + include/entt/core/algorithm.hpp | 143 + include/entt/core/any.hpp | 623 + include/entt/core/bit.hpp | 26 + include/entt/core/compressed_pair.hpp | 266 + include/entt/core/concepts.hpp | 17 + include/entt/core/enum.hpp | 102 + include/entt/core/family.hpp | 35 + include/entt/core/fwd.hpp | 51 + include/entt/core/hashed_string.hpp | 260 + include/entt/core/ident.hpp | 35 + include/entt/core/iterator.hpp | 181 + include/entt/core/memory.hpp | 225 + include/entt/core/monostate.hpp | 60 + include/entt/core/ranges.hpp | 22 + include/entt/core/tuple.hpp | 90 + include/entt/core/type_info.hpp | 232 + include/entt/core/type_traits.hpp | 909 + include/entt/core/utility.hpp | 84 + include/entt/entity/component.hpp | 59 + include/entt/entity/entity.hpp | 312 + include/entt/entity/fwd.hpp | 291 + include/entt/entity/group.hpp | 1052 + include/entt/entity/handle.hpp | 368 + include/entt/entity/helper.hpp | 256 + include/entt/entity/mixin.hpp | 593 + include/entt/entity/organizer.hpp | 437 + include/entt/entity/ranges.hpp | 28 + include/entt/entity/registry.hpp | 1181 + include/entt/entity/runtime_view.hpp | 322 + include/entt/entity/snapshot.hpp | 509 + include/entt/entity/sparse_set.hpp | 1076 + include/entt/entity/storage.hpp | 1222 + include/entt/entity/view.hpp | 1142 + include/entt/entt.hpp | 93 + include/entt/fwd.hpp | 11 + include/entt/graph/adjacency_matrix.hpp | 332 + include/entt/graph/dot.hpp | 56 + include/entt/graph/flow.hpp | 345 + include/entt/graph/fwd.hpp | 28 + include/entt/locator/locator.hpp | 161 + include/entt/meta/adl_pointer.hpp | 35 + include/entt/meta/container.hpp | 298 + include/entt/meta/context.hpp | 47 + include/entt/meta/factory.hpp | 656 + include/entt/meta/fwd.hpp | 43 + include/entt/meta/meta.hpp | 1902 ++ include/entt/meta/node.hpp | 287 + include/entt/meta/pointer.hpp | 42 + include/entt/meta/policy.hpp | 81 + include/entt/meta/range.hpp | 119 + include/entt/meta/resolve.hpp | 109 + include/entt/meta/template.hpp | 29 + include/entt/meta/type_traits.hpp | 54 + include/entt/meta/utility.hpp | 500 + include/entt/natvis/config.natvis | 3 + include/entt/natvis/container.natvis | 39 + include/entt/natvis/core.natvis | 32 + include/entt/natvis/entity.natvis | 181 + include/entt/natvis/graph.natvis | 19 + include/entt/natvis/locator.natvis | 3 + include/entt/natvis/meta.natvis | 200 + include/entt/natvis/poly.natvis | 6 + include/entt/natvis/process.natvis | 21 + include/entt/natvis/resource.natvis | 26 + include/entt/natvis/signal.natvis | 51 + include/entt/poly/fwd.hpp | 21 + include/entt/poly/poly.hpp | 316 + include/entt/process/fwd.hpp | 23 + include/entt/process/process.hpp | 316 + include/entt/process/scheduler.hpp | 227 + include/entt/resource/cache.hpp | 386 + include/entt/resource/fwd.hpp | 19 + include/entt/resource/loader.hpp | 33 + include/entt/resource/resource.hpp | 212 + include/entt/signal/delegate.hpp | 314 + include/entt/signal/dispatcher.hpp | 391 + include/entt/signal/emitter.hpp | 181 + include/entt/signal/fwd.hpp | 46 + include/entt/signal/sigh.hpp | 573 + include/entt/stl/algorithm.hpp | 22 + include/entt/stl/array.hpp | 19 + include/entt/stl/atomic.hpp | 18 + include/entt/stl/bit.hpp | 20 + include/entt/stl/cmath.hpp | 18 + include/entt/stl/concepts.hpp | 24 + include/entt/stl/cstddef.hpp | 21 + include/entt/stl/cstdint.hpp | 21 + include/entt/stl/functional.hpp | 55 + include/entt/stl/ios.hpp | 18 + include/entt/stl/iterator.hpp | 110 + include/entt/stl/limits.hpp | 18 + include/entt/stl/memory.hpp | 74 + include/entt/stl/ostream.hpp | 18 + include/entt/stl/sstream.hpp | 18 + include/entt/stl/string.hpp | 18 + include/entt/stl/string_view.hpp | 19 + include/entt/stl/tuple.hpp | 28 + include/entt/stl/type_traits.hpp | 70 + include/entt/stl/utility.hpp | 34 + include/entt/stl/vector.hpp | 18 + include/entt/tools.hpp | 3 + include/entt/tools/davey.hpp | 343 + include/nlohmann/json.hpp | 25526 ---------------- include/rapidjson/.clang-format | 2 + include/rapidjson/allocators.h | 693 + include/rapidjson/cursorstreamwrapper.h | 78 + include/rapidjson/document.h | 3044 ++ include/rapidjson/encodedstream.h | 299 + include/rapidjson/encodings.h | 716 + include/rapidjson/error/en.h | 176 + include/rapidjson/error/error.h | 285 + include/rapidjson/filereadstream.h | 99 + include/rapidjson/filewritestream.h | 104 + include/rapidjson/fwd.h | 151 + include/rapidjson/internal/biginteger.h | 297 + include/rapidjson/internal/clzll.h | 71 + include/rapidjson/internal/diyfp.h | 261 + include/rapidjson/internal/dtoa.h | 249 + include/rapidjson/internal/ieee754.h | 78 + include/rapidjson/internal/itoa.h | 308 + include/rapidjson/internal/meta.h | 186 + include/rapidjson/internal/pow10.h | 55 + include/rapidjson/internal/regex.h | 739 + include/rapidjson/internal/stack.h | 232 + include/rapidjson/internal/strfunc.h | 83 + include/rapidjson/internal/strtod.h | 293 + include/rapidjson/internal/swap.h | 46 + include/rapidjson/istreamwrapper.h | 128 + include/rapidjson/memorybuffer.h | 70 + include/rapidjson/memorystream.h | 71 + include/rapidjson/msinttypes/inttypes.h | 316 + include/rapidjson/msinttypes/stdint.h | 300 + include/rapidjson/ostreamwrapper.h | 81 + include/rapidjson/pointer.h | 1482 + include/rapidjson/prettywriter.h | 277 + include/rapidjson/rapidjson.h | 741 + include/rapidjson/reader.h | 2246 ++ include/rapidjson/schema.h | 3261 ++ include/rapidjson/stream.h | 223 + include/rapidjson/stringbuffer.h | 121 + include/rapidjson/uri.h | 481 + include/rapidjson/writer.h | 721 + src/CMakeLists.txt | 16 +- src/app.cpp | 20 +- src/camera.cpp | 6 +- src/debug_collector.cpp | 3 + src/dev_panel.cpp | 56 +- src/gameplay/block.cpp | 191 - src/gameplay/block_manager.cpp | 213 + src/gameplay/chunk.cpp | 49 + src/gameplay/chunk_generator.cpp | 33 + src/gameplay/client_chunk.cpp | 46 +- src/gameplay/client_entity_manager.cpp | 255 + src/gameplay/client_player_manager.cpp | 295 + src/gameplay/client_world.cpp | 327 +- src/gameplay/hitbox_manager.cpp | 121 + src/gameplay/item_manager.cpp | 131 + .../{client_player.cpp => local_player.cpp} | 616 +- src/gameplay/network_client.cpp | 31 +- src/gameplay/network_server.cpp | 8 +- src/gameplay/server_chunk.cpp | 1 + src/gameplay/server_entity_manager.cpp | 238 + src/gameplay/server_world.cpp | 302 +- src/gameplay/session.cpp | 16 +- src/gameplay/systems/physical_system.cpp | 168 + src/gameplay/systems/speed_system.cpp | 42 + src/gameplay/systems/wander_ai_system.cpp | 41 + src/localization.cpp | 21 +- src/proto/packet.proto | 1 + src/proto/world/entity.proto | 35 + src/render/model_manager.cpp | 153 + src/render/model_renderer.cpp | 157 + src/render/player_renderer.cpp | 141 +- src/render/renderer.cpp | 3 +- src/render/shader_manager.cpp | 13 +- src/render/vertex_array.cpp | 5 +- src/render/vertex_buffer.cpp | 5 + src/render/world_renderer.cpp | 168 +- src/scene/world_scene.cpp | 52 +- src/texture_manager.cpp | 40 +- src/tools/cubed_random.cpp | 11 + src/tools/json_utils.cpp | 54 + src/tools/model_loader.cpp | 124 + src/tools/sensitive_filter.cpp | 17 +- src/tools/shader_tools.cpp | 10 +- src/ui/credits_ui.cpp | 4 +- src/ui/inventory_ui.cpp | 74 +- src/ui/item_slot.cpp | 6 +- src/ui/world_ui_manager.cpp | 10 +- src/window.cpp | 4 +- vcpkg.json | 1 + 310 files changed, 49275 insertions(+), 26981 deletions(-) create mode 100644 AGENTS.md create mode 100644 assets/item/air.json create mode 100644 assets/item/dirt.json create mode 100644 assets/item/grass.json create mode 100644 assets/item/grass_block.json create mode 100644 assets/item/leaf.json create mode 100644 assets/item/log.json create mode 100644 assets/item/pig_spawn_egg.json create mode 100644 assets/item/sand.json create mode 100644 assets/item/snowy_grass_block.json create mode 100644 assets/item/stone.json create mode 100644 assets/item/water.json create mode 100644 assets/model/creature/pig/animation.json create mode 100644 assets/model/creature/pig/collision.json create mode 100644 assets/model/creature/pig/pig.glb create mode 100644 assets/model/creature/player/collision.json create mode 100644 assets/model/creature/player/player.glb rename assets/shaders/{depth_player_fragment_shader.glsl => depth_model_frag.glsl} (100%) create mode 100644 assets/shaders/depth_model_instance_vert.glsl rename assets/shaders/{depth_player_shader.glsl => depth_model_vert copy.glsl} (91%) create mode 100644 assets/shaders/depth_model_vert.glsl rename assets/shaders/{player_f_shader.glsl => model_frag.glsl} (100%) create mode 100644 assets/shaders/model_instance_vert.glsl rename assets/shaders/{player_v_shader.glsl => model_vert.glsl} (94%) create mode 100644 assets/sound/creature/pig/call.mp3 create mode 100644 assets/texture/item/spawn_egg/pig_spawn_egg.png delete mode 100644 include/Cubed/AABB.hpp create mode 100644 include/Cubed/gameplay/block_manager.hpp create mode 100644 include/Cubed/gameplay/chunk.hpp create mode 100644 include/Cubed/gameplay/client_entity_manager.hpp create mode 100644 include/Cubed/gameplay/client_player_manager.hpp create mode 100644 include/Cubed/gameplay/creatures/pig.hpp create mode 100644 include/Cubed/gameplay/creatures/spawn.hpp create mode 100644 include/Cubed/gameplay/ecs/ai_struct.hpp create mode 100644 include/Cubed/gameplay/ecs/animation.hpp create mode 100644 include/Cubed/gameplay/ecs/client_entity.hpp create mode 100644 include/Cubed/gameplay/ecs/entity.hpp create mode 100644 include/Cubed/gameplay/ecs/health.hpp create mode 100644 include/Cubed/gameplay/ecs/identity.hpp create mode 100644 include/Cubed/gameplay/ecs/movement.hpp create mode 100644 include/Cubed/gameplay/ecs/server_entity.hpp create mode 100644 include/Cubed/gameplay/ecs/state.hpp create mode 100644 include/Cubed/gameplay/ecs/transform.hpp rename include/Cubed/gameplay/{player.hpp => gait.hpp} (96%) create mode 100644 include/Cubed/gameplay/hitbox.hpp create mode 100644 include/Cubed/gameplay/hitbox_manager.hpp create mode 100644 include/Cubed/gameplay/item.hpp create mode 100644 include/Cubed/gameplay/item_manager.hpp create mode 100644 include/Cubed/gameplay/local_player.hpp create mode 100644 include/Cubed/gameplay/model.hpp create mode 100644 include/Cubed/gameplay/server_entity_manager.hpp create mode 100644 include/Cubed/gameplay/systems/physical_system.hpp create mode 100644 include/Cubed/gameplay/systems/speed_system.hpp create mode 100644 include/Cubed/gameplay/systems/wander_ai_system.hpp create mode 100644 include/Cubed/gameplay/world.hpp create mode 100644 include/Cubed/render/model_manager.hpp create mode 100644 include/Cubed/render/model_node.hpp create mode 100644 include/Cubed/render/model_renderer.hpp create mode 100644 include/Cubed/tools/cubed_concepts.hpp create mode 100644 include/Cubed/tools/json_utils.hpp create mode 100644 include/Cubed/tools/model_loader.hpp create mode 100644 include/Cubed/tools/name_space.hpp create mode 100644 include/Cubed/tools/net_utils.hpp create mode 100644 include/Cubed/tools/sparse_vector.hpp create mode 100644 include/Cubed/tools/threas_utils.hpp rename include/{nlohmann => entt}/.clang-format (100%) create mode 100644 include/entt/config/config.h create mode 100644 include/entt/config/macro.h create mode 100644 include/entt/config/version.h create mode 100644 include/entt/container/dense_map.hpp create mode 100644 include/entt/container/dense_set.hpp create mode 100644 include/entt/container/fwd.hpp create mode 100644 include/entt/container/table.hpp create mode 100644 include/entt/core/algorithm.hpp create mode 100644 include/entt/core/any.hpp create mode 100644 include/entt/core/bit.hpp create mode 100644 include/entt/core/compressed_pair.hpp create mode 100644 include/entt/core/concepts.hpp create mode 100644 include/entt/core/enum.hpp create mode 100644 include/entt/core/family.hpp create mode 100644 include/entt/core/fwd.hpp create mode 100644 include/entt/core/hashed_string.hpp create mode 100644 include/entt/core/ident.hpp create mode 100644 include/entt/core/iterator.hpp create mode 100644 include/entt/core/memory.hpp create mode 100644 include/entt/core/monostate.hpp create mode 100644 include/entt/core/ranges.hpp create mode 100644 include/entt/core/tuple.hpp create mode 100644 include/entt/core/type_info.hpp create mode 100644 include/entt/core/type_traits.hpp create mode 100644 include/entt/core/utility.hpp create mode 100644 include/entt/entity/component.hpp create mode 100644 include/entt/entity/entity.hpp create mode 100644 include/entt/entity/fwd.hpp create mode 100644 include/entt/entity/group.hpp create mode 100644 include/entt/entity/handle.hpp create mode 100644 include/entt/entity/helper.hpp create mode 100644 include/entt/entity/mixin.hpp create mode 100644 include/entt/entity/organizer.hpp create mode 100644 include/entt/entity/ranges.hpp create mode 100644 include/entt/entity/registry.hpp create mode 100644 include/entt/entity/runtime_view.hpp create mode 100644 include/entt/entity/snapshot.hpp create mode 100644 include/entt/entity/sparse_set.hpp create mode 100644 include/entt/entity/storage.hpp create mode 100644 include/entt/entity/view.hpp create mode 100644 include/entt/entt.hpp create mode 100644 include/entt/fwd.hpp create mode 100644 include/entt/graph/adjacency_matrix.hpp create mode 100644 include/entt/graph/dot.hpp create mode 100644 include/entt/graph/flow.hpp create mode 100644 include/entt/graph/fwd.hpp create mode 100644 include/entt/locator/locator.hpp create mode 100644 include/entt/meta/adl_pointer.hpp create mode 100644 include/entt/meta/container.hpp create mode 100644 include/entt/meta/context.hpp create mode 100644 include/entt/meta/factory.hpp create mode 100644 include/entt/meta/fwd.hpp create mode 100644 include/entt/meta/meta.hpp create mode 100644 include/entt/meta/node.hpp create mode 100644 include/entt/meta/pointer.hpp create mode 100644 include/entt/meta/policy.hpp create mode 100644 include/entt/meta/range.hpp create mode 100644 include/entt/meta/resolve.hpp create mode 100644 include/entt/meta/template.hpp create mode 100644 include/entt/meta/type_traits.hpp create mode 100644 include/entt/meta/utility.hpp create mode 100644 include/entt/natvis/config.natvis create mode 100644 include/entt/natvis/container.natvis create mode 100644 include/entt/natvis/core.natvis create mode 100644 include/entt/natvis/entity.natvis create mode 100644 include/entt/natvis/graph.natvis create mode 100644 include/entt/natvis/locator.natvis create mode 100644 include/entt/natvis/meta.natvis create mode 100644 include/entt/natvis/poly.natvis create mode 100644 include/entt/natvis/process.natvis create mode 100644 include/entt/natvis/resource.natvis create mode 100644 include/entt/natvis/signal.natvis create mode 100644 include/entt/poly/fwd.hpp create mode 100644 include/entt/poly/poly.hpp create mode 100644 include/entt/process/fwd.hpp create mode 100644 include/entt/process/process.hpp create mode 100644 include/entt/process/scheduler.hpp create mode 100644 include/entt/resource/cache.hpp create mode 100644 include/entt/resource/fwd.hpp create mode 100644 include/entt/resource/loader.hpp create mode 100644 include/entt/resource/resource.hpp create mode 100644 include/entt/signal/delegate.hpp create mode 100644 include/entt/signal/dispatcher.hpp create mode 100644 include/entt/signal/emitter.hpp create mode 100644 include/entt/signal/fwd.hpp create mode 100644 include/entt/signal/sigh.hpp create mode 100644 include/entt/stl/algorithm.hpp create mode 100644 include/entt/stl/array.hpp create mode 100644 include/entt/stl/atomic.hpp create mode 100644 include/entt/stl/bit.hpp create mode 100644 include/entt/stl/cmath.hpp create mode 100644 include/entt/stl/concepts.hpp create mode 100644 include/entt/stl/cstddef.hpp create mode 100644 include/entt/stl/cstdint.hpp create mode 100644 include/entt/stl/functional.hpp create mode 100644 include/entt/stl/ios.hpp create mode 100644 include/entt/stl/iterator.hpp create mode 100644 include/entt/stl/limits.hpp create mode 100644 include/entt/stl/memory.hpp create mode 100644 include/entt/stl/ostream.hpp create mode 100644 include/entt/stl/sstream.hpp create mode 100644 include/entt/stl/string.hpp create mode 100644 include/entt/stl/string_view.hpp create mode 100644 include/entt/stl/tuple.hpp create mode 100644 include/entt/stl/type_traits.hpp create mode 100644 include/entt/stl/utility.hpp create mode 100644 include/entt/stl/vector.hpp create mode 100644 include/entt/tools.hpp create mode 100644 include/entt/tools/davey.hpp delete mode 100644 include/nlohmann/json.hpp create mode 100644 include/rapidjson/.clang-format create mode 100644 include/rapidjson/allocators.h create mode 100644 include/rapidjson/cursorstreamwrapper.h create mode 100644 include/rapidjson/document.h create mode 100644 include/rapidjson/encodedstream.h create mode 100644 include/rapidjson/encodings.h create mode 100644 include/rapidjson/error/en.h create mode 100644 include/rapidjson/error/error.h create mode 100644 include/rapidjson/filereadstream.h create mode 100644 include/rapidjson/filewritestream.h create mode 100644 include/rapidjson/fwd.h create mode 100644 include/rapidjson/internal/biginteger.h create mode 100644 include/rapidjson/internal/clzll.h create mode 100644 include/rapidjson/internal/diyfp.h create mode 100644 include/rapidjson/internal/dtoa.h create mode 100644 include/rapidjson/internal/ieee754.h create mode 100644 include/rapidjson/internal/itoa.h create mode 100644 include/rapidjson/internal/meta.h create mode 100644 include/rapidjson/internal/pow10.h create mode 100644 include/rapidjson/internal/regex.h create mode 100644 include/rapidjson/internal/stack.h create mode 100644 include/rapidjson/internal/strfunc.h create mode 100644 include/rapidjson/internal/strtod.h create mode 100644 include/rapidjson/internal/swap.h create mode 100644 include/rapidjson/istreamwrapper.h create mode 100644 include/rapidjson/memorybuffer.h create mode 100644 include/rapidjson/memorystream.h create mode 100644 include/rapidjson/msinttypes/inttypes.h create mode 100644 include/rapidjson/msinttypes/stdint.h create mode 100644 include/rapidjson/ostreamwrapper.h create mode 100644 include/rapidjson/pointer.h create mode 100644 include/rapidjson/prettywriter.h create mode 100644 include/rapidjson/rapidjson.h create mode 100644 include/rapidjson/reader.h create mode 100644 include/rapidjson/schema.h create mode 100644 include/rapidjson/stream.h create mode 100644 include/rapidjson/stringbuffer.h create mode 100644 include/rapidjson/uri.h create mode 100644 include/rapidjson/writer.h create mode 100644 src/gameplay/block_manager.cpp create mode 100644 src/gameplay/chunk.cpp create mode 100644 src/gameplay/client_entity_manager.cpp create mode 100644 src/gameplay/client_player_manager.cpp create mode 100644 src/gameplay/hitbox_manager.cpp create mode 100644 src/gameplay/item_manager.cpp rename src/gameplay/{client_player.cpp => local_player.cpp} (52%) create mode 100644 src/gameplay/server_entity_manager.cpp create mode 100644 src/gameplay/systems/physical_system.cpp create mode 100644 src/gameplay/systems/speed_system.cpp create mode 100644 src/gameplay/systems/wander_ai_system.cpp create mode 100644 src/proto/world/entity.proto create mode 100644 src/render/model_manager.cpp create mode 100644 src/render/model_renderer.cpp create mode 100644 src/tools/json_utils.cpp create mode 100644 src/tools/model_loader.cpp diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a6457c9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,251 @@ +# Code Modification Guidelines + +## 1. Git Branch Protection + +- Never modify code directly on the `main` branch. +- Before editing, check the current branch: + +```bash +git branch --show-current +```` + +* If the result is `main`, create or switch to a development branch first: + +```bash +git checkout -b feature/xxx +``` + +* Do not bypass branch protection by: + + * Committing directly to `main`. + * Running code generation, formatting, or large refactors on `main`. + * Rewriting or damaging `main` history. + +--- + +## 2. Code Comments + +* All new comments must be written in English. +* Comments must be: + + * Short and clear. + * Explain the purpose, not repeat the code. + * Avoid unnecessary details. + +Recommended: + +```cpp +// AI-generated: Prevent stale handle access. +``` + +Avoid: + +```cpp +// AI-generated: This checks if the handle is invalid because... +``` + +* Important AI-added or modified logic should include: + +```cpp +// AI-generated +``` + +* Do not add comments for trivial changes such as: + + * One or two line logic fixes. + * Spelling corrections. + * Simple variable renames. + +### 2.1 Do not write excessive or granular comments + +AI must avoid sprinkling small explanatory comments throughout code. +Specifically: + +* **No line-by-line narration.** Do not add a comment above (or beside) + every field, signal, slot, branch, or block just to restate what it is. + If the name is self-explanatory, no comment is needed. +* **No multi-part explanations on a single marker.** A line like + `// AI-generated: abort the in-flight download; finished handler clears state.` + is too much. Either drop it, or shorten to a single short clause + (e.g. `// AI-generated: abort in-flight download.`). +* **Prefer zero or one comment per file/section**, not many. A short + header comment explaining the file's purpose is acceptable; dozens of + inline labels are not. +* **Do not comment obvious QML/C++ bindings, layout splits, or default + values** (e.g. `// AI-generated: index 0 -> zh_CN, index 1 -> en.`). + The code already says that. +* **When in doubt, omit the comment.** A missing comment is fine; a + redundant one is noise that future maintainers must clean up. + +Rule of thumb: if removing the comment would not lose useful +information, remove it. + +--- + +## 3. Project Style + +Follow the existing project style strictly: + +* Naming conventions. +* File organization. +* Formatting rules. +* Include order. +* Existing architecture. + +Do not: + +* Introduce unrelated coding styles. +* Add unnecessary design patterns. +* Create duplicate systems. +* Add abstractions without need. +* Change module responsibilities. + +--- + +## 4. Modification Scope + +Only modify the minimum code required. + +Do not: + +* Format unrelated files. +* Remove existing comments. +* Rename large numbers of symbols. +* Change public APIs. +* Perform large refactors without confirmation. + +If large changes are required, explain first: + +1. Current problem. +2. Required changes. +3. Affected modules. +4. Expected impact. + +--- + +## 5. Build and Test + +After modifications: + +* Verify compilation. +* Check for new warnings. +* Ensure existing features still work. + +Do not: + +* Commit without verification. +* Ignore compiler errors. +* Hide problems with temporary hacks. + +--- + +## 6. Compatibility + +Consider: + +* Existing callers. +* API/ABI compatibility. +* Serialization formats. +* Network protocols. +* Save data. +* Threading impact. + +Do not: + +* Change network structures without compatibility handling. +* Break old data formats. +* Change public data layouts carelessly. + +--- + +## 7. Multi-threading Safety + +For multi-threaded code, check: + +* Data races. +* Object lifetime. +* Lock contention. +* Atomic correctness. +* Cross-thread resource access. + +Do not: + +* Assume single-threaded execution. +* Modify shared data without protection. +* Add hidden locks that hurt performance. + +--- + +## 8. Memory Safety + +Consider: + +* Ownership. +* Lifetime. +* RAII. +* Memory leaks. +* Dangling references. +* Iterator invalidation. + +Do not: + +* Return references to local variables. +* Store pointers to temporary objects. +* Use uninitialized data. +* Introduce undefined behavior. + +--- + +## 9. Third-party Libraries + +Before adding or changing dependencies: + +Check: + +* Existing project dependencies. +* Build system impact. +* Maintenance cost. + +Do not: + +* Add large libraries for simple features. +* Duplicate existing library functionality. +* Modify third-party source code. + +--- + +## 10. AI Behavior Rules + +AI-generated code must: + +* Reuse existing code first. +* Preserve current architecture. +* Prefer simple solutions. +* Minimize risk and code changes. + +Do not: + +* Guess requirements. +* Add unrequested features. +* Redesign architecture. +* Add unnecessary abstraction layers. + +Choose the implementation with: + +> The smallest change, lowest risk, and best compatibility with existing code. + +--- + +## 11. Pre-commit Checklist + +Before committing: + +* [ ] Not on `main`. +* [ ] Changes match the requested task. +* [ ] New comments are in English. +* [ ] AI changes are marked when needed. +* [ ] No unrelated formatting changes. +* [ ] Build passes. +* [ ] No obvious performance/security/thread issues. +* [ ] No API, protocol, or data format breakage. + diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e765f4..19601e4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -148,6 +148,7 @@ target_link_libraries(${PROJECT_NAME} OpenAL::OpenAL harfbuzz::harfbuzz Opus::Opus + assimp::assimp $<$:ws2_32> ) diff --git a/assets/data/block/air.toml b/assets/data/block/air.toml index d3a07c9..3b833c2 100644 --- a/assets/data/block/air.toml +++ b/assets/data/block/air.toml @@ -8,5 +8,4 @@ is_passable = true is_transitional = false is_transparent = true name = 'air' -name_key = 'block.air' roughness = 1.0 \ No newline at end of file diff --git a/assets/data/block/dirt.toml b/assets/data/block/dirt.toml index 8de0d17..d7f3c5b 100644 --- a/assets/data/block/dirt.toml +++ b/assets/data/block/dirt.toml @@ -8,5 +8,4 @@ is_passable = false is_transitional = true is_transparent = false name = 'dirt' -name_key = 'block.dirt' roughness = 1.0 \ No newline at end of file diff --git a/assets/data/block/grass.toml b/assets/data/block/grass.toml index ad4998c..688a14c 100644 --- a/assets/data/block/grass.toml +++ b/assets/data/block/grass.toml @@ -8,5 +8,4 @@ is_passable = true is_transitional = false is_transparent = true name = 'grass' -name_key = 'block.grass' roughness = 0.90000000000000002 \ No newline at end of file diff --git a/assets/data/block/grass_block.toml b/assets/data/block/grass_block.toml index c998df8..3918475 100644 --- a/assets/data/block/grass_block.toml +++ b/assets/data/block/grass_block.toml @@ -8,5 +8,4 @@ is_passable = false is_transitional = true is_transparent = false name = 'grass_block' -name_key = 'block.grass_block' roughness = 0.90000000000000002 \ No newline at end of file diff --git a/assets/data/block/leaf.toml b/assets/data/block/leaf.toml index 5c93fcf..186c03f 100644 --- a/assets/data/block/leaf.toml +++ b/assets/data/block/leaf.toml @@ -8,5 +8,4 @@ is_passable = false is_transitional = false is_transparent = true name = 'leaf' -name_key = 'block.leaf' roughness = 0.69999999999999996 \ No newline at end of file diff --git a/assets/data/block/log.toml b/assets/data/block/log.toml index 169f9ad..b38b4e9 100644 --- a/assets/data/block/log.toml +++ b/assets/data/block/log.toml @@ -8,5 +8,4 @@ is_passable = false is_transitional = false is_transparent = false name = 'log' -name_key = 'block.log' roughness = 0.69999999999999996 \ No newline at end of file diff --git a/assets/data/block/sand.toml b/assets/data/block/sand.toml index fe411c7..c00bd1f 100644 --- a/assets/data/block/sand.toml +++ b/assets/data/block/sand.toml @@ -8,5 +8,4 @@ is_passable = false is_transitional = true is_transparent = false name = 'sand' -name_key = 'block.sand' roughness = 0.80000000000000004 \ No newline at end of file diff --git a/assets/data/block/snowy_grass_block.toml b/assets/data/block/snowy_grass_block.toml index 220b93f..109d65a 100644 --- a/assets/data/block/snowy_grass_block.toml +++ b/assets/data/block/snowy_grass_block.toml @@ -8,5 +8,4 @@ is_passable = false is_transitional = true is_transparent = false name = 'snowy_grass_block' -name_key = 'block.snowy_grass_block' roughness = 0.90000000000000002 \ No newline at end of file diff --git a/assets/data/block/stone.toml b/assets/data/block/stone.toml index e39d214..c0c7016 100644 --- a/assets/data/block/stone.toml +++ b/assets/data/block/stone.toml @@ -8,5 +8,4 @@ is_passable = false is_transitional = true is_transparent = false name = 'stone' -name_key = 'block.stone' roughness = 0.75 \ No newline at end of file diff --git a/assets/data/block/template.toml b/assets/data/block/template.toml index 34c62ce..30911fa 100644 --- a/assets/data/block/template.toml +++ b/assets/data/block/template.toml @@ -9,4 +9,3 @@ is_discard = false is_blend = false is_transitional = false roughness = 1.0 -name_key = "block.template" diff --git a/assets/data/block/water.toml b/assets/data/block/water.toml index 33bec27..0cd0e4e 100644 --- a/assets/data/block/water.toml +++ b/assets/data/block/water.toml @@ -8,5 +8,4 @@ is_passable = true is_transitional = false is_transparent = true name = 'water' -name_key = 'block.water' roughness = 0.02 \ No newline at end of file diff --git a/assets/item/air.json b/assets/item/air.json new file mode 100644 index 0000000..ab8272d --- /dev/null +++ b/assets/item/air.json @@ -0,0 +1,6 @@ +{ + "id": 0, + "name": "air", + "description": "", + "type": "block" +} \ No newline at end of file diff --git a/assets/item/dirt.json b/assets/item/dirt.json new file mode 100644 index 0000000..70f9e19 --- /dev/null +++ b/assets/item/dirt.json @@ -0,0 +1,7 @@ +{ + "id": 2, + "name": "dirt", + "description": "", + "texture": "texture/item/block/dirt.png", + "type": "block" +} \ No newline at end of file diff --git a/assets/item/grass.json b/assets/item/grass.json new file mode 100644 index 0000000..228d566 --- /dev/null +++ b/assets/item/grass.json @@ -0,0 +1,7 @@ +{ + "id": 9, + "name": "grass", + "description": "", + "texture": "texture/item/block/grass.png", + "type": "block" +} \ No newline at end of file diff --git a/assets/item/grass_block.json b/assets/item/grass_block.json new file mode 100644 index 0000000..e00e186 --- /dev/null +++ b/assets/item/grass_block.json @@ -0,0 +1,7 @@ +{ + "id": 1, + "name": "grass_block", + "description": "", + "texture": "texture/item/block/grass_block.png", + "type": "block" +} \ No newline at end of file diff --git a/assets/item/leaf.json b/assets/item/leaf.json new file mode 100644 index 0000000..6263836 --- /dev/null +++ b/assets/item/leaf.json @@ -0,0 +1,7 @@ +{ + "id": 6, + "name": "leaf", + "description": "", + "texture": "texture/item/block/leaf.png", + "type": "block" +} \ No newline at end of file diff --git a/assets/item/log.json b/assets/item/log.json new file mode 100644 index 0000000..aeceded --- /dev/null +++ b/assets/item/log.json @@ -0,0 +1,7 @@ +{ + "id": 5, + "name": "log", + "description": "", + "texture": "texture/item/block/log.png", + "type": "block" +} \ No newline at end of file diff --git a/assets/item/pig_spawn_egg.json b/assets/item/pig_spawn_egg.json new file mode 100644 index 0000000..6df5ead --- /dev/null +++ b/assets/item/pig_spawn_egg.json @@ -0,0 +1,8 @@ +{ + "id": 10, + "name": "pig_spawn_egg", + "description": "", + "texture": "texture/item/spawn_egg/pig_spawn_egg.png", + "type": "spawn_egg", + "creature": "cubed:pig" +} \ No newline at end of file diff --git a/assets/item/sand.json b/assets/item/sand.json new file mode 100644 index 0000000..5d022c0 --- /dev/null +++ b/assets/item/sand.json @@ -0,0 +1,7 @@ +{ + "id": 4, + "name": "sand", + "description": "", + "texture": "texture/item/block/sand.png", + "type": "block" +} \ No newline at end of file diff --git a/assets/item/snowy_grass_block.json b/assets/item/snowy_grass_block.json new file mode 100644 index 0000000..f1eb8f1 --- /dev/null +++ b/assets/item/snowy_grass_block.json @@ -0,0 +1,7 @@ +{ + "id": 8, + "name": "snowy_grass_block", + "description": "", + "texture": "texture/item/block/snowy_grass_block.png", + "type": "block" +} \ No newline at end of file diff --git a/assets/item/stone.json b/assets/item/stone.json new file mode 100644 index 0000000..8e177b8 --- /dev/null +++ b/assets/item/stone.json @@ -0,0 +1,7 @@ +{ + "id": 3, + "name": "stone", + "description": "", + "texture": "texture/item/block/stone.png", + "type": "block" +} \ No newline at end of file diff --git a/assets/item/water.json b/assets/item/water.json new file mode 100644 index 0000000..0f6e7ee --- /dev/null +++ b/assets/item/water.json @@ -0,0 +1,7 @@ +{ + "id": 7, + "name": "water", + "description": "", + "texture": "texture/item/block/water.png", + "type": "block" +} \ No newline at end of file diff --git a/assets/lang/en_US.json b/assets/lang/en_US.json index 4444ab9..bd45502 100644 --- a/assets/lang/en_US.json +++ b/assets/lang/en_US.json @@ -36,14 +36,15 @@ "joingame.server_ip": "Server Ip", "joingame.join_world": "Join World", "error.disable_voice": "Server Disable Voice Chat", - "block.air": "air", - "block.dirt": "dirt", - "block.grass_block": "grass block", - "block.grass": "grass", - "block.leaf": "leaf", - "block.log": "log", - "block.sand": "sand", - "block.snowy_grass_block": "snowy grass block", - "block.stone": "stone", - "block.water": "water" + "item.air.name": "air", + "item.dirt.name": "dirt", + "item.grass_block.name": "grass block", + "item.grass.name": "grass", + "item.leaf.name": "leaf", + "item.log.name": "log", + "item.sand.name": "sand", + "item.snowy_grass_block.name": "snowy grass block", + "item.stone.name": "stone", + "item.water.name": "water", + "item.pig_spawn_egg.name": "pig spawn egg" } \ No newline at end of file diff --git a/assets/lang/zh_CN.json b/assets/lang/zh_CN.json index 1d83700..362fb53 100644 --- a/assets/lang/zh_CN.json +++ b/assets/lang/zh_CN.json @@ -36,14 +36,15 @@ "joingame.server_ip": "服务器IP", "joingame.join_world": "加入世界", "error.disable_voice": "服务器已禁用语音聊天", - "block.air": "空气", - "block.dirt": "泥土", - "block.grass_block": "草方块", - "block.grass": "草", - "block.leaf": "树叶", - "block.log": "原木", - "block.sand": "沙子", - "block.snowy_grass_block": "覆雪草方块", - "block.stone": "石头", - "block.water": "水" + "item.air.name": "空气", + "item.dirt.name": "泥土", + "item.grass_block.name": "草方块", + "item.grass.name": "草", + "item.leaf.name": "树叶", + "item.log.name": "原木", + "item.sand.name": "沙子", + "item.snowy_grass_block.name": "覆雪草方块", + "item.stone.name": "石头", + "item.water.name": "水", + "item.pig_spawn_egg.name": "猪猪刷怪蛋" } \ No newline at end of file diff --git a/assets/model/creature/pig/animation.json b/assets/model/creature/pig/animation.json new file mode 100644 index 0000000..53859c2 --- /dev/null +++ b/assets/model/creature/pig/animation.json @@ -0,0 +1,33 @@ +{ + "walk": { + "speed": 6.0, + "amplitude": 25.0 + }, + "run": { + "speed": 12.0, + "amplitude": 40.0 + }, + "body_bob": 0.03, + "head": { + "node": "Head", + "amplitude": 4.0 + }, + "legs": [ + { + "node": "Foot_FL", + "phase": 0.0 + }, + { + "node": "Foot_FR", + "phase": 3.14159 + }, + { + "node": "Foot_HL", + "phase": 3.14159 + }, + { + "node": "Foot_HR", + "phase": 0.0 + } + ] +} \ No newline at end of file diff --git a/assets/model/creature/pig/collision.json b/assets/model/creature/pig/collision.json new file mode 100644 index 0000000..69607ea --- /dev/null +++ b/assets/model/creature/pig/collision.json @@ -0,0 +1,16 @@ +{ + "boxes": [ + { + "center": [ + 0, + 0.78, + 0 + ], + "half": [ + 0.53, + 0.78, + 0.406 + ] + } + ] +} \ No newline at end of file diff --git a/assets/model/creature/pig/pig.glb b/assets/model/creature/pig/pig.glb new file mode 100644 index 0000000000000000000000000000000000000000..b94f8d9f5cdbde09adc2e071fea37ff1ea4e7c95 GIT binary patch literal 15992 zcmeHN30xEBwjYAXB35uguWL-T58Hs5gphzm!XlLif&vd0u1bIa(U4#gROI1JD;BEP ztJYqv%T-ZZ-&L__tG%^?VAUcnwbiS&^(xnW->MaPD({@h%rIHB0n*>E{XOT$%sF$u z|98&!opZh|LyjgThDQ*@%;5ylIDjDjGdVG#NT4(rR7OE)kzl$?Z&2&Bf>41tP$Uov za#UKCUTM_nK@zFaWlqgdX)|;A(m*mWh@S(x_^O%tI=xY)7ZeKxhD?AP1fgj~06?tAwrTAl9Gu+fmWHP0&N+rw#jA{f?_D7GzdaP0HYNY35vzn5Q!2LW?O9Dk>J*Rul`#7Whb&G7I3DxoS<8UIjf)6HT;N$!ty3 zQqjT=X|D=NwM-}qB;_(r10!`=g_ah@&$AdJkx7NmvnjS?ld)*77#M6}I7X*4PL7GU zv>ntY+Y(#bbReL+FEE~DX*{@1#)E8)o0EXCZIcsJ&qrGIT;3+*Qd{FzJ%5gMtFDu6 zNwZ*E<02s$2vLTo*LZf>5zsE)*D*`W&1eBtap;Vsm-2QLoN|aZFO#_#9nSWv6MA%7QK1MutFR zUeGo7p|IIhEx>&h!#VimlqxE~k@0q!ax_28*m3*BVEW!E&d1 zD#SW!?edgZEOnx%#6dyr@RV38cBZGqa_g|Q%Tp3jh!Z^}v6&ZIlT#8&uroa+3APSf z`#cpQaiXV!Y^H|Rcq&LNa;B#szpWCyU7iY(IdeXTmC34ct?`spy0ptv z(qL!K=u(@hp;exeThHhc`HR-sV6k;vNRi}4dj^@rCTYblG9Z)M_*yD%+3(PM9nOwM zCXT$!1vG8lAv?rsQoN7RleQC}-k{ zf!TITgW*IX3WO&p&Qs%Qg4rAb6;Y*7Vx*J_Qb$Bd;JC!>P0U6~)bEHj$hqbd^5K%{vmPU(h8ZEJ>mJ(8)!l}^gDKVb>%q_-a4XradDk452$wn*q z*RjTm0;Rb5MbFa_kjc$wLN*3w00e=g5Wp~ymVVg=L+WS?+7t{bl^hARC{QlO4nYUa zOp6nLF*GuJU1)b`U|MrDG6iEGebh!9sl-b%C^O%6%#=lV`6upw?8JW7~#$yCZyeg z@4T3_Vg_hqOzaNycE<#dSB?&FXU9Zrcj!AUCgN610d0$k*zQ1Ye@t32!0DJsf~cQH zsXY}t`O*srOl|BcPfyM-T2NqJbuc5^G<%tMj1DgI^e)D{3ex+eUtHqFc87ZFqk?`^ z!cX{WrN+D&&d<<~QW=#RjXE<)SCEqnKk7q45&X7p7RD$usZRnhr1VTVBZE}WP#RQG zI*m@BqMB(e(8K<@NTAkcsbI@2DlUedwUdSo(8OE_|*D%-7~nhXoc2$;Lo|Wxg!IcNSYH%#jQfSQ@HP<=7%&05M2l zX`~i@RyRYT-L!pkyYrMeF=`Ed0SgV0;aL=huccsyUYVZ^zn)7(GH4~frOy@s6k5zlAa)ar`TVT@1Q8jV0RIiJ7XBN+ zCma|K&xd!Eo2m$c%Tt(YjvRQbOJQi9i(z;c13ImFE{5U(gLb(*qn~XL*s_b^>IV#K zi^em0P|oP*@~lpdeiMyFyhHpRN;UN`Ilw!tm)i~p^m1+gnl>;`tS^*tZ+~bzzE{#% zhN2vKEMqW8@#11o#$Zv-+)N#e3+fQjbs36sCT5HbDPoyAE(HEh43BUnIuLF|M|gH5 z+zAiDlkkG47tx9ECOQ+Z!1EQN3*kff5?$fhmFPxvCwdTm@bn{kf(8%Ji|rY?hr`1= z%K6nG=kkbwo0S7k)kO@$17$HZ&td>)!*emn1BTMiKv_9r;AZg*&&41Qa!Nm!2br~9yBNT+`k_9T zw`nu2pKUYLWpzRs!~dRhoJu(Q@oV)1<7Is{fDGkGaojKzap~~RP?Y04&Si*W=v`Wl z6muWtNbw!(FqDy_49oFCo}tJqXgtcW9$vYCMLE)!!p=0iHW{xIBxgrZGUV z?#ppx&MkaejI})+`3rdTq4m`OGL$1d#-WHyhj)fzIlkj~KpaCc-%*bAkG#Z%8~wm8b_7TU;M2K`%dxnxrmQ)E{~XMSZf(M@GPF;xfq&fF;LHj=VFit z45gpThuPY-i{a{L+GD3K)dpHWmuGcCU52;Ft>@(&SMFf`adYqYY{LioUITt_fSaKx zV{QWEBJ>YX0x4cJMLE{Pve7h#q9CH=K=FMoU9JF%a)zS3hJHtCeIX9zCc2EF4YVAo zbIwQajZS#0vsZ5~XhG-Lkoyy#PTDf6~y2qH~e|p#B;Iie{Ha)w3N}9CpdtK_- z`5T^$yI(gXdG6*BZU=`vXzp|QeQ*Aj-DN!aoxhxaa5(6}iGs;pOnaB?j~^X2#%gzt@h%xr`$%#Yl5;N;W(#L6vE zWoNo45qYauyUrxy6`>}Noddg=-ciW!txEIa&!{bG)~s)=KDPc!%=*T~v4@*(Opfp_ zo>%o>?)7U%Uwb8{exWqu^+oyRJpbm}I^~zCJGb~Izg^O)haz&%fz-*SZ|>B5I&^<$ zf@p58y`T3#;L48ZLsufL>9yNS$BbVD5+Zg)LiEkT3lI*rR?&LSQ9j8w5 zKRbQ%Ka(C6y}uyr(9>^pi9`MVR2^PL^oe_1dSOjU!qF$wRz@XEdcCw*KP~b6S8vQ- zeecPmo8hbeHEPxzV(Ie1x(m8n=_8tt+{-T+TtCS};akUh((B}|KkP~T-1m)vyB9qC zu-gE)k-jIhzbLHNDF=Qtd|d5c?oB1V)^?Vkp4@xY&1Qb_>tVhA)+mxcRe0>*wlRO( z7e1zbktJ3B7c1QAcx4ktuaVb$Qp;CdU65F^X5p9h;k!0BKP>qtvEl6lJEr@r%_jGr z*z(4B*TVY)r$odX8Vw>ZpJA%1zFC=th81NMJ=Ec!$OT0BWO0ADW9y^|sXH!DC`n%! zBJk!lCcdNbGG1@|+$);c@w7AVk7<2(-4F9HELiflK5icW;>De9nm*^`vo9AeoM9OA z-EltA?9B_<9Wy;{W0!Lg#HwC9W4`|>-m9T!{xH+RN1=g!YsaqJR=tF$x%OF><_44f7*c<#qyFqW z=ZnEUo6l`J8BNrcti5qm;@WA&dfz?oj`Nz``NE0b*FFD!=FH>wh;2s-Cv=djJI%{o zuw!3x<>SVSukMvDN}iYUESea6d{LPC@h;CPNsj}B(&XGRU*GZVZW>hd-Py70yGAZ8 zs8V)U45}2oJ=8zDqGZty!{F*EGt;)em3Q0UG`KwG`;=+>EBB>EN_T&h>RoYtAul)T zT(p0+F}Zxjw(1YkrVi~;ce+lt{@#SW7ax4MGI_83jWybyt5(zuDYnsbAGcPd*;{cA8-6kB9iC;-9{Y9Ntv_b(9&G8`El{3Ew%Dbx#|cE)l)D^{ngh;f)^MT%r@Q z_|Kjlzv5amw(sUY3NK6|eQTC@_>MhVvAVkH(2n(lPsU>5a7BkLbM>>ri{%S=ikN*_ zu?^QvK|9ARdoBMF*;(~$O&nbq^9AzH5gHg%jhz3aOt*5sM)4LGtfvD4)@ zi`)&8|6RIs)6MYog8?fl@^6J+-~X)n#~;7F=s#i3;yYgYA#v_EH=SH}pR#oPQcKepItn2VEv#C86e?w{{H~>M)d*! literal 0 HcmV?d00001 diff --git a/assets/model/creature/player/collision.json b/assets/model/creature/player/collision.json new file mode 100644 index 0000000..2db6de1 --- /dev/null +++ b/assets/model/creature/player/collision.json @@ -0,0 +1,16 @@ +{ + "boxes": [ + { + "center": [ + 0, + 0.9, + 0 + ], + "half": [ + 0.3, + 0.9, + 0.3 + ] + } + ] +} \ No newline at end of file diff --git a/assets/model/creature/player/player.glb b/assets/model/creature/player/player.glb new file mode 100644 index 0000000000000000000000000000000000000000..b94f8d9f5cdbde09adc2e071fea37ff1ea4e7c95 GIT binary patch literal 15992 zcmeHN30xEBwjYAXB35uguWL-T58Hs5gphzm!XlLif&vd0u1bIa(U4#gROI1JD;BEP ztJYqv%T-ZZ-&L__tG%^?VAUcnwbiS&^(xnW->MaPD({@h%rIHB0n*>E{XOT$%sF$u z|98&!opZh|LyjgThDQ*@%;5ylIDjDjGdVG#NT4(rR7OE)kzl$?Z&2&Bf>41tP$Uov za#UKCUTM_nK@zFaWlqgdX)|;A(m*mWh@S(x_^O%tI=xY)7ZeKxhD?AP1fgj~06?tAwrTAl9Gu+fmWHP0&N+rw#jA{f?_D7GzdaP0HYNY35vzn5Q!2LW?O9Dk>J*Rul`#7Whb&G7I3DxoS<8UIjf)6HT;N$!ty3 zQqjT=X|D=NwM-}qB;_(r10!`=g_ah@&$AdJkx7NmvnjS?ld)*77#M6}I7X*4PL7GU zv>ntY+Y(#bbReL+FEE~DX*{@1#)E8)o0EXCZIcsJ&qrGIT;3+*Qd{FzJ%5gMtFDu6 zNwZ*E<02s$2vLTo*LZf>5zsE)*D*`W&1eBtap;Vsm-2QLoN|aZFO#_#9nSWv6MA%7QK1MutFR zUeGo7p|IIhEx>&h!#VimlqxE~k@0q!ax_28*m3*BVEW!E&d1 zD#SW!?edgZEOnx%#6dyr@RV38cBZGqa_g|Q%Tp3jh!Z^}v6&ZIlT#8&uroa+3APSf z`#cpQaiXV!Y^H|Rcq&LNa;B#szpWCyU7iY(IdeXTmC34ct?`spy0ptv z(qL!K=u(@hp;exeThHhc`HR-sV6k;vNRi}4dj^@rCTYblG9Z)M_*yD%+3(PM9nOwM zCXT$!1vG8lAv?rsQoN7RleQC}-k{ zf!TITgW*IX3WO&p&Qs%Qg4rAb6;Y*7Vx*J_Qb$Bd;JC!>P0U6~)bEHj$hqbd^5K%{vmPU(h8ZEJ>mJ(8)!l}^gDKVb>%q_-a4XradDk452$wn*q z*RjTm0;Rb5MbFa_kjc$wLN*3w00e=g5Wp~ymVVg=L+WS?+7t{bl^hARC{QlO4nYUa zOp6nLF*GuJU1)b`U|MrDG6iEGebh!9sl-b%C^O%6%#=lV`6upw?8JW7~#$yCZyeg z@4T3_Vg_hqOzaNycE<#dSB?&FXU9Zrcj!AUCgN610d0$k*zQ1Ye@t32!0DJsf~cQH zsXY}t`O*srOl|BcPfyM-T2NqJbuc5^G<%tMj1DgI^e)D{3ex+eUtHqFc87ZFqk?`^ z!cX{WrN+D&&d<<~QW=#RjXE<)SCEqnKk7q45&X7p7RD$usZRnhr1VTVBZE}WP#RQG zI*m@BqMB(e(8K<@NTAkcsbI@2DlUedwUdSo(8OE_|*D%-7~nhXoc2$;Lo|Wxg!IcNSYH%#jQfSQ@HP<=7%&05M2l zX`~i@RyRYT-L!pkyYrMeF=`Ed0SgV0;aL=huccsyUYVZ^zn)7(GH4~frOy@s6k5zlAa)ar`TVT@1Q8jV0RIiJ7XBN+ zCma|K&xd!Eo2m$c%Tt(YjvRQbOJQi9i(z;c13ImFE{5U(gLb(*qn~XL*s_b^>IV#K zi^em0P|oP*@~lpdeiMyFyhHpRN;UN`Ilw!tm)i~p^m1+gnl>;`tS^*tZ+~bzzE{#% zhN2vKEMqW8@#11o#$Zv-+)N#e3+fQjbs36sCT5HbDPoyAE(HEh43BUnIuLF|M|gH5 z+zAiDlkkG47tx9ECOQ+Z!1EQN3*kff5?$fhmFPxvCwdTm@bn{kf(8%Ji|rY?hr`1= z%K6nG=kkbwo0S7k)kO@$17$HZ&td>)!*emn1BTMiKv_9r;AZg*&&41Qa!Nm!2br~9yBNT+`k_9T zw`nu2pKUYLWpzRs!~dRhoJu(Q@oV)1<7Is{fDGkGaojKzap~~RP?Y04&Si*W=v`Wl z6muWtNbw!(FqDy_49oFCo}tJqXgtcW9$vYCMLE)!!p=0iHW{xIBxgrZGUV z?#ppx&MkaejI})+`3rdTq4m`OGL$1d#-WHyhj)fzIlkj~KpaCc-%*bAkG#Z%8~wm8b_7TU;M2K`%dxnxrmQ)E{~XMSZf(M@GPF;xfq&fF;LHj=VFit z45gpThuPY-i{a{L+GD3K)dpHWmuGcCU52;Ft>@(&SMFf`adYqYY{LioUITt_fSaKx zV{QWEBJ>YX0x4cJMLE{Pve7h#q9CH=K=FMoU9JF%a)zS3hJHtCeIX9zCc2EF4YVAo zbIwQajZS#0vsZ5~XhG-Lkoyy#PTDf6~y2qH~e|p#B;Iie{Ha)w3N}9CpdtK_- z`5T^$yI(gXdG6*BZU=`vXzp|QeQ*Aj-DN!aoxhxaa5(6}iGs;pOnaB?j~^X2#%gzt@h%xr`$%#Yl5;N;W(#L6vE zWoNo45qYauyUrxy6`>}Noddg=-ciW!txEIa&!{bG)~s)=KDPc!%=*T~v4@*(Opfp_ zo>%o>?)7U%Uwb8{exWqu^+oyRJpbm}I^~zCJGb~Izg^O)haz&%fz-*SZ|>B5I&^<$ zf@p58y`T3#;L48ZLsufL>9yNS$BbVD5+Zg)LiEkT3lI*rR?&LSQ9j8w5 zKRbQ%Ka(C6y}uyr(9>^pi9`MVR2^PL^oe_1dSOjU!qF$wRz@XEdcCw*KP~b6S8vQ- zeecPmo8hbeHEPxzV(Ie1x(m8n=_8tt+{-T+TtCS};akUh((B}|KkP~T-1m)vyB9qC zu-gE)k-jIhzbLHNDF=Qtd|d5c?oB1V)^?Vkp4@xY&1Qb_>tVhA)+mxcRe0>*wlRO( z7e1zbktJ3B7c1QAcx4ktuaVb$Qp;CdU65F^X5p9h;k!0BKP>qtvEl6lJEr@r%_jGr z*z(4B*TVY)r$odX8Vw>ZpJA%1zFC=th81NMJ=Ec!$OT0BWO0ADW9y^|sXH!DC`n%! zBJk!lCcdNbGG1@|+$);c@w7AVk7<2(-4F9HELiflK5icW;>De9nm*^`vo9AeoM9OA z-EltA?9B_<9Wy;{W0!Lg#HwC9W4`|>-m9T!{xH+RN1=g!YsaqJR=tF$x%OF><_44f7*c<#qyFqW z=ZnEUo6l`J8BNrcti5qm;@WA&dfz?oj`Nz``NE0b*FFD!=FH>wh;2s-Cv=djJI%{o zuw!3x<>SVSukMvDN}iYUESea6d{LPC@h;CPNsj}B(&XGRU*GZVZW>hd-Py70yGAZ8 zs8V)U45}2oJ=8zDqGZty!{F*EGt;)em3Q0UG`KwG`;=+>EBB>EN_T&h>RoYtAul)T zT(p0+F}Zxjw(1YkrVi~;ce+lt{@#SW7ax4MGI_83jWybyt5(zuDYnsbAGcPd*;{cA8-6kB9iC;-9{Y9Ntv_b(9&G8`El{3Ew%Dbx#|cE)l)D^{ngh;f)^MT%r@Q z_|Kjlzv5amw(sUY3NK6|eQTC@_>MhVvAVkH(2n(lPsU>5a7BkLbM>>ri{%S=ikN*_ zu?^QvK|9ARdoBMF*;(~$O&nbq^9AzH5gHg%jhz3aOt*5sM)4LGtfvD4)@ zi`)&8|6RIs)6MYog8?fl@^6J+-~X)n#~;7F=s#i3;yYgYA#v_EH=SH}pR#oPQcKepItn2VEv#C86e?w{{H~>M)d*! literal 0 HcmV?d00001 diff --git a/assets/shaders/depth_player_fragment_shader.glsl b/assets/shaders/depth_model_frag.glsl similarity index 100% rename from assets/shaders/depth_player_fragment_shader.glsl rename to assets/shaders/depth_model_frag.glsl diff --git a/assets/shaders/depth_model_instance_vert.glsl b/assets/shaders/depth_model_instance_vert.glsl new file mode 100644 index 0000000..dd0ecab --- /dev/null +++ b/assets/shaders/depth_model_instance_vert.glsl @@ -0,0 +1,13 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; +layout (location = 3) in mat4 modelMatrix; +uniform mat4 lightSpaceMatrix; + +out vec2 tc; + +void main() { + tc = texCoord; + gl_Position = lightSpaceMatrix * modelMatrix * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/depth_player_shader.glsl b/assets/shaders/depth_model_vert copy.glsl similarity index 91% rename from assets/shaders/depth_player_shader.glsl rename to assets/shaders/depth_model_vert copy.glsl index 2de921a..93f577b 100644 --- a/assets/shaders/depth_player_shader.glsl +++ b/assets/shaders/depth_model_vert copy.glsl @@ -7,7 +7,6 @@ uniform mat4 lightSpaceMatrix; uniform mat4 modelMatrix; out vec2 tc; -flat out int tex_layer; void main() { tc = texCoord; diff --git a/assets/shaders/depth_model_vert.glsl b/assets/shaders/depth_model_vert.glsl new file mode 100644 index 0000000..93f577b --- /dev/null +++ b/assets/shaders/depth_model_vert.glsl @@ -0,0 +1,14 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; + +uniform mat4 lightSpaceMatrix; +uniform mat4 modelMatrix; + +out vec2 tc; + +void main() { + tc = texCoord; + gl_Position = lightSpaceMatrix * modelMatrix * vec4(pos, 1.0); +} \ No newline at end of file diff --git a/assets/shaders/player_f_shader.glsl b/assets/shaders/model_frag.glsl similarity index 100% rename from assets/shaders/player_f_shader.glsl rename to assets/shaders/model_frag.glsl diff --git a/assets/shaders/model_instance_vert.glsl b/assets/shaders/model_instance_vert.glsl new file mode 100644 index 0000000..cded335 --- /dev/null +++ b/assets/shaders/model_instance_vert.glsl @@ -0,0 +1,26 @@ +#version 460 + +layout (location = 0) in vec3 pos; +layout (location = 1) in vec2 texCoord; +layout (location = 2) in vec3 aNormal; +layout (location = 3) in mat4 modelMatrix; +uniform mat4 view_matrix; +uniform mat4 proj_matrix; +uniform mat4 lightSpaceMatrix; +out vec4 FragPosLightSpace; +out vec3 normal; +out vec2 tc; +out vec3 vert_pos; + + +void main() { + vec4 worldPos = modelMatrix * vec4(pos, 1.0); + FragPosLightSpace = lightSpaceMatrix * worldPos; + mat4 mv_matrix= view_matrix * modelMatrix; + mat4 norm_matrix = transpose(inverse(mv_matrix)); + vec4 viewPos = mv_matrix * vec4(pos, 1.0); + tc = texCoord; + vert_pos = pos; + normal = normalize(mat3(norm_matrix) * aNormal); + gl_Position = proj_matrix * viewPos; +} \ No newline at end of file diff --git a/assets/shaders/player_v_shader.glsl b/assets/shaders/model_vert.glsl similarity index 94% rename from assets/shaders/player_v_shader.glsl rename to assets/shaders/model_vert.glsl index d42b89a..961e8de 100644 --- a/assets/shaders/player_v_shader.glsl +++ b/assets/shaders/model_vert.glsl @@ -3,7 +3,6 @@ layout (location = 0) in vec3 pos; layout (location = 1) in vec2 texCoord; layout (location = 2) in vec3 aNormal; -layout (location = 3) in vec3 aTangent; uniform mat4 mv_matrix; uniform mat4 proj_matrix; diff --git a/assets/sound/creature/pig/call.mp3 b/assets/sound/creature/pig/call.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..1075145fdb066b28e644fde6bed9991df7745d55 GIT binary patch literal 3779 zcmc(i`8U*GAHY8|mY6YPkTrzHHpZH4!`HryE!h%@lBGl=qO2pbW#2<-vW_ier);Az z%95p0GE$TxODN3eiJs@2=MQ+!d0yw9d*APK&s|>k-d`@>;5Z|&&oNsY8^b>>I{+}4 zpAYmqQxudAdjD47j!xHqrGG=bugBFt(VuYu2LK?m0fBv_4^Tb8@BrHbE*wBO zAmV_eeew^eIH2W#X9r9i@bQ4v1HgUuSN^j}w?CUyQrl1d0~)IHH$-N4j9bke#Ndt< z1%Mv1y3o?aEepAHOo%zr6#!f-!~;G1ix@}`XuV}S8I^d}RW(vTq?;&X!d&TnTO>zS z}XCwgXv$dR0dn^&15FOZ661!Bwwb55)NUFFDgzMlCZs!D$iB)%h!RVM`ju>) z8R>jk^mUhy0WK7XZ1Hkzkd~<$qsEPuUVDS7DHqH~@vh{*f8`g@^25B#wLLn+RbFC^ z9FsJPg+e-1qQA+9Fet~`;>2r~P_i{ps%$-4H+#D1UD#hCc<3YjvSN$w0Pe+$n_4&0 ze{vA)(MD>Bx$LTGTU-5-yj6ztv=O8a!}ajAj(ZlElJC>%&8($w#S)M0)!~Z2J*nAS zPh2>b{$QMNetj4|f^#AzCl5brHEkwW(DNq7gg!R6u4z)F}%K^6syc?NE#E zmc;l8-i)<%@y%4jm5)a$qMm#G276*O*;E~%%(=gvS5)P+0Qxr2CR>^uKSo}kVBT(vO+*P=)^7p%GAKT8f*LgS@B;;*9I{z}U zAP3xbF`_5g)<07>t#_BN&pc(RFlp|B9%{uO2oc%TUig>g+OG!XVO460(AT})5%Bu$ zycD$-p@jxDrY@Tvql&wPSsFC=MM@u|8Nisbl2Q!@|DqjfzsQXoxv9K}0(kO8E*b?g z3p$80ek-&?>Ob?wuw;n4{d#|N)BA7$bBU$(vOlRbyz8uxVpF={O`B&M z=9;JVPyw2GgC^exNc<=V4dLu8t1QWJeMG0rt(=ss&v?7Ms0646x$=Y`N2nn+-iTB2 zZs3UAO2n_1#txQFpkYC}Dsv#W2CsfCe0*LF#byP|`KeDAO6*9M2w(wNeh8rELED|%S5eBS4N)$1U5{_xJz^NJoW z15a#lex+sYf*{y?eFt1eEo!;!&1TxfqaaKJ%&8B!c-ZdDQaA*SIkYaGnn z1LWdZEF=oOFQrXlHH{!7IRo_Z{?Uylf9a4a!!2Ho-s4WYoR5_oL2&iK`lEy+3VFA_ zfv*~Qt~+gDAe7BkA|wZ*G{zUryf4H|grBW+snQ&a#OGa6-ayQ~LnG^-cwIU-r&m4p z%4R0l7dAsaoO=Wy#X`u~CHa!{QAc}cMO{}3cTN?ayX({vTncs~r?7dIAj_qn=qM>b zP$WS-g~D&cIxp~!z>^)!L^wYc8E!Fsd&eMSX3iykllwRrVkovRgdoBSs-3V3KHYfkij7|G7KK}9pgJvjh^#^n(#Acc zI9^jy$%-{z+4)ziJ@y6C_@N`Tg?yuNTI;^8QexdS*Ip?k{#2qkz4+9VNowJ%x6GX3Wj5*>1wKm zdlq)mjpR+0^Bi3X5qwHSc#g*ia67Q{yr>6mp!x`VECk3u#*N9xC-Vptvn%-SMQk>) zxSw|wl4?@u$S|2E>vNe%U|4Bu#d&oU2*7WUHY0|+gKhh%GoK(E9JftwBvLePvE|)(pN#hiICJE?A;XulxC3_-O? zE?{xBx_8KILQm=k(Q$7xwhd?8=c{7x>#Z>$b9+Wh7^bi2qu?Owo{|}{NBnN@s5S4Vl`}pBcCv$KYC88?sk;=V-JIC z&d#yp;z}(yMExzDvY)F!7jsMDMYAos>gd%Bhx1ezqcRUdrn%_0p|P zhJSo`(9QXktasu54QUZsN6OBW6}zXY7B8*Fumr*+)t8#iR#rGC%_K^xn!WdSX`U)5 zSJFDuXo;p?(+BM?8&?G9Ubl}t9nP-D@xxV`c5eaykA zQg`i!$KI#ooDz2aL0i+(v-MJ{@#TK?t%|IV77HJ1kS~T-NBC`uYinbd-}+SQ0G}b< z{d#hi8;m;{d!Z7jMCa-u_H0X~{FrG=Cx|!sADi)PYj!|OMxw!d!qOacrmqU|f z&w{5Pb}e!2Z$GuUFJuA0w0O-vv)M#y99~rJMYQk{qH8$GKZAUl#zcU80L#U6o8e1Z zed`yFsxiMT%q^0&6Lkq?@z8b%zttq+^_|E2wIE{#g;nBoO--5>=7q~qpYO05<$IuH zRk@YhYoV+}K69Nfp0pC2@3fPE2-OGu4@(@XHhTywAQo3mW)r`nZ7)-iw1p2-am11J zF3d*ek+QMAS?ukcpbXjS9rz}#u{rBE4=H)ei>QI07Y--&1`i^U6;0u0!R%7bZnQIql z8832488)nD`$Ap->&VurQZ}nJ10k;s3d}#cXs#Ara_!UP+bmrUX&cGLN9LgEaR`uz z5RTG13jR9$gAtH^ruKY7YUte;{i)YrVtBjaZJqA;-dNl6^tmdf@hFiXD~v!jm5_;s z(W=%`Zd@I_Rp$EmcVrQji}TogrXZ#)F^kD(%+Xxe!cbXG;qTYze^KuX1p*1ER!Q;N Uz;0>Iv-1im!2e?6|JNM<0xM)JivR!s literal 0 HcmV?d00001 diff --git a/assets/texture/item/spawn_egg/pig_spawn_egg.png b/assets/texture/item/spawn_egg/pig_spawn_egg.png new file mode 100644 index 0000000000000000000000000000000000000000..2688620a9e837a2a91a260529b56b253e6a35f82 GIT binary patch literal 174 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|GCW-zLo9le z6C^SYba+3yew6wD#Gg68_#&<*iMm|xIWNG&7&EI-@=%NK#iw_7cM2UYU{t)lac2|j z?56Ve{=};qDcP-xZCANpbZyL3$za&Xu$=M4y#r5G* - -namespace Cubed { - -struct AABB { - glm::vec3 min{0.0f, 0.0f, 0.0f}; - glm::vec3 max{0.0f, 0.0f, 0.0f}; - - AABB(glm::vec3 min_point, glm::vec3 max_point) - : min(min_point), max(max_point) {} - - bool intersects(const AABB& other) const { - return (min.x <= other.max.x && max.x >= other.min.x) && - (min.y <= other.max.y && max.y >= other.min.y) && - (min.z <= other.max.z && max.z >= other.min.z); - } -}; - -} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/app.hpp b/include/Cubed/app.hpp index 4d9ee45..9fd6c7c 100644 --- a/include/Cubed/app.hpp +++ b/include/Cubed/app.hpp @@ -49,7 +49,6 @@ private: Window m_window; TextureManager m_texture_manager; - AudioEngine m_audio; Renderer m_renderer; diff --git a/include/Cubed/argument.hpp b/include/Cubed/argument.hpp index 4f4ac31..79b29c2 100644 --- a/include/Cubed/argument.hpp +++ b/include/Cubed/argument.hpp @@ -13,5 +13,6 @@ struct Argument { std::optional log_level; std::optional enable_filelog; std::optional enable_consolelog; + std::optional direct_enter; }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/camera.hpp b/include/Cubed/camera.hpp index ff40f72..5fe9769 100644 --- a/include/Cubed/camera.hpp +++ b/include/Cubed/camera.hpp @@ -8,7 +8,7 @@ namespace Cubed { -class ClientPlayer; +class LocalPlayer; class Camera { private: @@ -18,7 +18,7 @@ private: THIRD_PERSON_FRONT, }; - ClientPlayer* m_player; + LocalPlayer* m_player; float m_last_mouse_x, m_last_mouse_y; glm::vec3 m_camera_pos; bool m_under_water = false; @@ -35,7 +35,7 @@ public: void update_move_camera(); - void camera_init(ClientPlayer* player); + void camera_init(LocalPlayer* player); void hot_reload(); void update_cursor_position_camera(float offset_x, float offset_y); @@ -47,7 +47,7 @@ public: void change_perspective(); bool is_first_person() const; bool handle_event(const Event& e); - ClientPlayer* player(); + LocalPlayer* player(); }; } // namespace Cubed diff --git a/include/Cubed/dev_panel.hpp b/include/Cubed/dev_panel.hpp index 6b4303c..28a6935 100644 --- a/include/Cubed/dev_panel.hpp +++ b/include/Cubed/dev_panel.hpp @@ -7,7 +7,7 @@ namespace Cubed { class WorldScene; -class ClientPlayer; +class LocalPlayer; class App; class DevPanel { struct ConfigView { @@ -42,7 +42,7 @@ private: WorldScene& m_world_scene; Config& m_config; ConfigView m_config_view; - ClientPlayer* m_player; + LocalPlayer* m_player; PlayerProfile m_player_profile; bool m_need_save_config = false; bool m_gen_thread_running = true; diff --git a/include/Cubed/gameplay/block.hpp b/include/Cubed/gameplay/block.hpp index bc4499d..8a8056f 100644 --- a/include/Cubed/gameplay/block.hpp +++ b/include/Cubed/gameplay/block.hpp @@ -42,7 +42,6 @@ struct LookBlock { struct BlockData { std::string name; - std::string name_key; BlockType id = 0; bool is_liquid = false; @@ -56,42 +55,14 @@ struct BlockData { bool is_blend = false; bool is_transitional = false; float roughness = 1.0f; - BlockData(std::string_view b_name, std::string_view name_k, BlockType b_id, - bool liquid, bool passable, bool cross_plane, bool transparent, - bool gas, bool discard, bool blend, bool transitional, float r) - : name(b_name), name_key(name_k), id(b_id), is_liquid(liquid), - is_gas(gas), is_passable(passable), is_cross_plane(cross_plane), + BlockData(std::string_view b_name, BlockType b_id, bool liquid, + bool passable, bool cross_plane, bool transparent, bool gas, + bool discard, bool blend, bool transitional, float r) + : name(b_name), id(b_id), is_liquid(liquid), is_gas(gas), + is_passable(passable), is_cross_plane(cross_plane), is_transparent(transparent), is_discard(discard), is_blend(blend), is_transitional(transitional), roughness(r) {} -}; - -class BlockManager { - -public: - static const std::vector& datas(); - static void init(); - static unsigned sums(); - static unsigned cross_plane_sum(); - static const std::string& name_form_id(BlockType id); - static std::string local_name(BlockType id); - static bool is_gas(BlockType id); - static bool is_liquid(BlockType id); - - static bool is_cross_plane(BlockType id); - static bool is_transparent(BlockType id); - static bool is_passable(BlockType id); - - static bool is_discard(BlockType id); - static bool is_blend(BlockType id); - static bool is_transitional(BlockType id); - static float roughness(BlockType id); - static BlockType cross_plane_index(BlockType id); - -private: - static void set_up_cross_plane_map(); - static inline std::vector m_datas; - static inline bool is_init = false; - static inline std::unordered_map m_cross_plane_map; + BlockData() { name = ""; } }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/block_manager.hpp b/include/Cubed/gameplay/block_manager.hpp new file mode 100644 index 0000000..4fb8e28 --- /dev/null +++ b/include/Cubed/gameplay/block_manager.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include "Cubed/gameplay/block.hpp" + +#include +namespace Cubed { +class BlockManager { + +public: + static void init(); + static unsigned sums(); + static unsigned cross_plane_sum(); + static const std::string& name_form_id(BlockType id); + static bool is_gas(BlockType id); + static bool is_liquid(BlockType id); + + static bool is_cross_plane(BlockType id); + static bool is_transparent(BlockType id); + static bool is_passable(BlockType id); + + static bool is_discard(BlockType id); + static bool is_blend(BlockType id); + static bool is_transitional(BlockType id); + static float roughness(BlockType id); + static BlockType cross_plane_index(BlockType id); + + static BlockType id_from_name(const std::string& name); + +private: + using BlockMap = tbb::concurrent_hash_map; + using acc = BlockMap::accessor; + using cacc = BlockMap::const_accessor; + using IDMap = tbb::concurrent_hash_map; + using CrossPlaneMap = tbb::concurrent_hash_map; + + static inline const BlockData EMPTY; + + static inline BlockMap m_datas; + static inline IDMap m_id_map; + static inline bool is_init = false; + static inline CrossPlaneMap m_cross_plane_map; + static void set_up_cross_plane_map( + const std::vector>& types); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/chunk.hpp b/include/Cubed/gameplay/chunk.hpp new file mode 100644 index 0000000..f2ef335 --- /dev/null +++ b/include/Cubed/gameplay/chunk.hpp @@ -0,0 +1,27 @@ +#pragma once +#include "Cubed/gameplay/chunk_pos.hpp" + +#include +#include +namespace Cubed { +class Chunk { +public: + Chunk() = default; + Chunk(const Chunk&) = delete; + Chunk(Chunk&&) = delete; + Chunk& operator=(const Chunk&) = delete; + Chunk& operator=(Chunk&&) = delete; + virtual ~Chunk() = default; + static int index(int x, int y, int z); + static int index(const glm::vec3& pos); + static std::tuple world_to_block(int world_x, int world_y, + int world_z, int chunk_x, + int chunk_z); + static std::tuple world_to_block(const glm::ivec3& block_pos, + ChunkPos chunk_pos); + static std::tuple block_to_world(int x, int y, int z, + int chunk_x, int chunk_z); + static std::tuple block_to_world(const glm::ivec3& block_pos, + ChunkPos chunk_pos); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/chunk_generator.hpp b/include/Cubed/gameplay/chunk_generator.hpp index 27faca9..a61f4f6 100644 --- a/include/Cubed/gameplay/chunk_generator.hpp +++ b/include/Cubed/gameplay/chunk_generator.hpp @@ -50,6 +50,8 @@ public: void generate_cave(); void generate_river(); + void spawn_creature(); + private: static inline std::atomic is_init{false}; static inline unsigned m_generator_seed{0}; diff --git a/include/Cubed/gameplay/client_chunk.hpp b/include/Cubed/gameplay/client_chunk.hpp index a4fe258..52a2952 100644 --- a/include/Cubed/gameplay/client_chunk.hpp +++ b/include/Cubed/gameplay/client_chunk.hpp @@ -2,6 +2,7 @@ #include "Cubed/constants.hpp" #include "Cubed/gameplay/biome.hpp" #include "Cubed/gameplay/block.hpp" +#include "Cubed/gameplay/chunk.hpp" #include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/vertex_data.hpp" #include "world/chunk_data.pb.h" @@ -26,7 +27,7 @@ struct ChunkRenderSnapshot { glm::vec3 center; glm::vec3 half_extents; }; -class ClientChunk { +class ClientChunk : public Chunk { public: ClientChunk(ClientWorld& world); ~ClientChunk(); @@ -35,17 +36,6 @@ public: ClientChunk(ClientChunk&&) noexcept; ClientChunk& operator=(ClientChunk&&) noexcept; - static int index(int x, int y, int z); - static int index(const glm::vec3& pos); - static std::tuple world_to_block(int world_x, int world_y, - int world_z, int chunk_x, - int chunk_z); - static std::tuple world_to_block(const glm::ivec3& block_pos, - ChunkPos chunk_pos); - static std::tuple block_to_world(int x, int y, int z, - int chunk_x, int chunk_z); - static std::tuple block_to_world(const glm::ivec3& block_pos, - ChunkPos chunk_pos); BiomeType get_biome() const; ChunkPos get_chunk_pos() const; const std::vector& get_chunk_blocks() const; diff --git a/include/Cubed/gameplay/client_entity_manager.hpp b/include/Cubed/gameplay/client_entity_manager.hpp new file mode 100644 index 0000000..1230edf --- /dev/null +++ b/include/Cubed/gameplay/client_entity_manager.hpp @@ -0,0 +1,82 @@ +#pragma once +#include "Cubed/gameplay/ecs/entity.hpp" +#include "Cubed/gameplay/gait.hpp" +#include "Cubed/tools/cubed_random.hpp" +#include "glm/ext/vector_float3.hpp" +#include "world/entity.pb.h" + +#include +#include +#include +namespace Cubed { +class ClientWorld; +class ClientEntityManager { +public: + enum class Command { CREATE, DESTORY, UPDATE }; + + ClientEntityManager(ClientWorld& world); + void update(float dt); + void init(); + + void receive_entity_create(S2CEntityCreate& msg); + void receive_entity_destory(EntityID id); + void receive_entity_update(const S2CEntityUpdate& msg); + void receive_entity_update(S2CEntityUpdateBatch& msg); + void destory(EntityID id); + void create(std::string_view name, const glm::vec3& pos); + + const entt::registry& get_registry() const; + + void player_sound(float dt); + +private: + struct EntityCreateElement { + EntityID id; + std::string name; + glm::vec3 pos; + }; + + struct UpdateInfo { + EntityID id; + glm::vec3 pos; + glm::vec3 direction; + Gait gait; + }; + + using EntityMap = tbb::concurrent_hash_map; + using acc = EntityMap::accessor; + using cacc = EntityMap::const_accessor; + using CreateFunc = std::function; + using TaskElement = std::variant; + using TaskPair = std::pair; + + ClientWorld& m_world; + entt::registry m_registry; + EntityMap m_entities; + std::unordered_map m_factories; + tbb::concurrent_queue m_tasks; + Random m_random; + void handle_task(float dt); + void handle_entity_destory(EntityID id); + // not thread safe + void handle_entity_create(EntityID id, std::string_view name, + const glm::vec3& pos); + void handle_entity_update(UpdateInfo& info, float dt); + template + void create_entity_in_registry(EntityID id, Args&&... args) { + { + cacc a; + if (m_entities.find(a, id)) { + return; + } + } + auto entity = m_registry.create(); + + ((m_registry.emplace>( + entity, std::forward(args))), + ...); + m_entities.emplace(id, entity); + return; + } +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/client_player.hpp b/include/Cubed/gameplay/client_player.hpp index 0ab5c12..267402a 100644 --- a/include/Cubed/gameplay/client_player.hpp +++ b/include/Cubed/gameplay/client_player.hpp @@ -1,167 +1,36 @@ #pragma once -#include "Cubed/AABB.hpp" -#include "Cubed/constants.hpp" -#include "Cubed/gameplay/block.hpp" -#include "Cubed/gameplay/chunk_pos.hpp" -#include "Cubed/gameplay/game_mode.hpp" -#include "Cubed/gameplay/game_time.hpp" -#include "Cubed/gameplay/item_stack.hpp" -#include "Cubed/gameplay/player.hpp" -#include "Cubed/input/event.hpp" -#include "Cubed/input/input.hpp" -#include -#include -#include -#include +#include "Cubed/gameplay/ecs/animation.hpp" +#include "Cubed/gameplay/ecs/identity.hpp" +#include "Cubed/gameplay/ecs/transform.hpp" + +#include namespace Cubed { -class ClientWorld; -class ClientPlayer { -public: - static constexpr size_t HOTBAR_SUM = 10; - static constexpr float WALK_SOUND_INTERVAL = 0.45f; - static constexpr float RUN_SOUND_INTERVAL = 0.3f; - using ChunkPosSet = absl::flat_hash_set; - ClientPlayer(ClientWorld& world); - ~ClientPlayer(); - - bool handle_mouse_button_event(const MouseButtonEvent& e); - bool handle_key_event(const KeyEvent& e); - bool handle_mouse_wheel_event(const MouseWheelEvent& e); - - void update_front_vec(float offset_x, float offset_y); - bool update_player_move_state(Key key, KeyAction action); - bool update_scroll(float yoffset); - - void update_chunk_set(const ChunkPosSet& set); - const ChunkPosSet& get_chunk_pos_set() const; - ChunkPosSet get_chunk_pos_set(); - - static AABB get_aabb(const glm::vec3& pos); - const glm::vec3& get_front() const; - Gait get_gait() const; - const std::optional& get_look_block_pos() const; - // thread safe - glm::vec3 get_player_pos() const; - const MoveState& get_move_state() const; - - void change_mode(GameMode mode); - void reload_config(); - void set_player_pos(const glm::vec3& pos); - void update(float delta_time); - - float& max_walk_speed(); - float& max_run_speed(); - float& max_speed(); - float& acceleration(); - float& deceleration(); - float& g(); - float& fly_y_speed(); - - const ItemStack& get_current_itemstack() const; - - void set_gait(Gait gait); - GameMode& game_mode(); - - ClientWorld& get_world(); - - void set_uuid(std::string_view uuid); - std::string get_uuid() const; - const std::string& get_name() const; - void reset_key_status(); - void init(std::string_view name); - - float yaw() const; - float pitch() const; - float& angle(); - float& walk_time(); - bool ray_cast(const glm::vec3& start, const glm::vec3& dir, - glm::ivec3& block_pos, glm::vec3& normal, - float distance = 4.0f); - bool is_underwater() const; - void set_underwater(bool u); - void place_block(float dt); - - int selected_hotbar() const; - void set_hotbar(int pos, const ItemStack& item); - std::span get_hotbar() const; - -private: - using enum GameMode; - float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED; - float m_max_run_speed = DEFAULT_MAX_RUN_SPEED; - float m_acceleration = DEFAULT_ACCELERATION; - float m_deceleration = DEFAULT_DECELERATION; - float m_g = DEFAULT_G; - static constexpr float MAX_SPACE_ON_TIME = 0.3f; - static constexpr float PLACE_BLOCK_INTERVAL = 0.2f; - - float m_place_time = PLACE_BLOCK_INTERVAL; - std::atomic m_yaw = 0.0f; - std::atomic m_pitch = 0.0f; - std::array m_hotbar; - float m_sensitivity = 0.15f; - - float m_max_speed = m_max_walk_speed; - float m_y_speed = 0.0f; - float m_fly_y_speed = 7.5f; - bool can_up = true; - - float space_on_time = 0.0f; - bool space_on = false; - bool is_fly = false; - - float m_xz_speed = 0.0f; - - int m_selected_hotbar = 0; - - bool m_moving = false; - bool m_sprinting = false; - bool m_underwater = false; - - glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f); - glm::vec3 move_distance{0.0f, 0.0f, 0.0f}; - // player is tow block tall, the pos is the lower pos - - glm::vec3 m_player_pos{0.0f, 255.0f, 0.0f}; - ChunkPos m_last_chunk_pos{0, 0}; - - glm::vec3 m_front{0, 0, -1}; - glm::vec3 m_right{0, 0, 0}; - static constexpr glm::vec3 M_SIZE{0.6f, 1.8f, 0.6f}; - - std::atomic m_gait = Gait::STOP; - MoveState m_move_state{}; - MouseState m_mouse_state{}; - GameMode m_game_mode = CREATIVE; - std::optional m_look_block = std::nullopt; - std::string m_name{}; - mutable std::shared_mutex m_uuid_mutex; - std::string m_uuid; - ClientWorld& m_world; - - float m_angle{0.0f}; - float m_walk_time{0.0f}; - - std::unordered_map m_timers; - - mutable std::shared_mutex m_player_pos_mutex; - mutable std::shared_mutex m_chunk_pos_mutex; - ChunkPosSet m_player_chunk_pos_set; - - void update_direction(); - void update_lookup_block(); - - void update_move(float delta_time); - - void update_x_move(glm::vec3& player_pos); - void update_y_move(glm::vec3& player_pos); - void update_z_move(glm::vec3& player_pos); - - void update_player_chunk(); - - void play_walk_sound(float dt); - Gait compute_gait() const; +struct ClientPlayerSnapshot { + double time_ms = 0.0f; + glm::vec3 pos{0.0f}; + float yaw = 0.0f; + float pitch = 0.0f; }; -} // namespace Cubed +struct ClientPlayerState { + std::deque value; +}; +struct ClientPlayer { + Position pos{}; + EntityInfo entity{}; + WalkPose walk{}; + Orientation angle{}; + Position render_pos{}; + Orientation render_angle{}; + ClientPlayerState history{}; +}; + +struct PlayerRenderData { + EntityInfo info{}; + Position render_pos{}; + Orientation angle{}; + Gait gait{}; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/client_player_manager.hpp b/include/Cubed/gameplay/client_player_manager.hpp new file mode 100644 index 0000000..a5888b8 --- /dev/null +++ b/include/Cubed/gameplay/client_player_manager.hpp @@ -0,0 +1,45 @@ +#pragma once +#include "Cubed/gameplay/client_player.hpp" +#include "Cubed/gameplay/local_player.hpp" +#include "Cubed/gameplay/network_client.hpp" +#include "Cubed/tools/sparse_vector.hpp" +namespace Cubed { +class ClientWorld; +class ClientPlayerManager { +public: + ClientPlayerManager(const ClientPlayerManager&) = delete; + ClientPlayerManager(ClientPlayerManager&&) = delete; + ClientPlayerManager& operator=(const ClientPlayerManager&) = delete; + ClientPlayerManager& operator=(ClientPlayerManager&&) = delete; + ClientPlayerManager(ClientWorld& world); + ~ClientPlayerManager(); + + void init(std::string_view local_name); + void update(float dt); + + std::span render_player_data(); + + bool has_player(const Hitbox& hitbox) const; + + LocalPlayer& get_local(); + const LocalPlayer& get_local() const; + + void receive_remote_player(const PlayerInfoRsp& rsp); + void receive_player_logout(const LogoutRsp& rsp); + + void reload_config(); + + void report_player_info(NetworkClient* client); + +private: + ClientWorld& m_world; + mutable std::shared_mutex m_players_mutex; + using PlayerHandle = SparseVector::Handle; + SparseVector m_players; + std::vector m_render_data; + std::unordered_map m_players_handle; + LocalPlayer m_local; + + void update_players_data(float dt); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/client_world.hpp b/include/Cubed/gameplay/client_world.hpp index 1c50df6..3040b22 100644 --- a/include/Cubed/gameplay/client_world.hpp +++ b/include/Cubed/gameplay/client_world.hpp @@ -5,9 +5,12 @@ #include "Cubed/gameplay/chat_message.hpp" #include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/client_chunk.hpp" -#include "Cubed/gameplay/client_player.hpp" +#include "Cubed/gameplay/client_entity_manager.hpp" +#include "Cubed/gameplay/client_player_manager.hpp" #include "Cubed/gameplay/game_time.hpp" +#include "Cubed/gameplay/local_player.hpp" #include "Cubed/gameplay/network_client.hpp" +#include "Cubed/gameplay/world.hpp" #include "Cubed/input/event.hpp" #include "Cubed/tools/cubed_random.hpp" #include "Cubed/tools/priority_thread_pool.hpp" @@ -19,46 +22,26 @@ #include namespace Cubed { -struct PlayerInfo { - std::string name; - std::string uuid; - glm::vec3 render_pos; - glm::vec3 target_pos; - float render_yaw; - float yaw; - float render_pitch; - float pitch; - Gait gait; - float angle = 0.0f; - float walk_time = 0.0f; - float moving_time = 0.0f; -}; - -struct PlayerRenderData { - std::string name; - std::string uuid; - glm::vec3 render_pos; - float yaw; - float pitch; - Gait gait; - float angle; -}; class WorldScene; -class ClientWorld { +class ClientWorld : public World { public: + ClientWorld(const ClientWorld&) = delete; + ClientWorld(ClientWorld&&) = delete; + ClientWorld& operator=(const ClientWorld&) = delete; + ClientWorld& operator=(ClientWorld&&) = delete; ClientWorld(AudioEngine& auido, Config& config, WorldScene& scene); ~ClientWorld(); void init(std::string_view player_name, - std::shared_ptr client); - void update(float delta_time); + std::shared_ptr client, RunMode mode); + void update(float dt); bool handle_event(const Event& e); const std::optional& get_look_block_pos() const; - ClientPlayer& get_player(); - const ClientPlayer& get_player() const; - int get_block(const glm::ivec3& block_pos) const; - bool is_solid(const glm::ivec3& block_pos) const; - bool can_pass_block(const glm::ivec3& block_pos) const; - BlockType get_block_tpye(const glm::ivec3& block_pos) const; + LocalPlayer& get_player(); + const LocalPlayer& get_player() const; + int get_block(const glm::ivec3& block_pos) const override; + bool is_solid(const glm::ivec3& block_pos) const override; + bool can_pass_block(const glm::ivec3& block_pos) const override; + BlockType get_block_tpye(const glm::ivec3& block_pos) const override; void rebuild_world(); @@ -71,7 +54,6 @@ public: void receive_block_change(const BlockChangeRsp& rsp); void receive_time(const UpdateTime& rsp); - void receive_remote_player(const PlayerInfoRsp& rsp); void receive_player_logout(const LogoutRsp& rsp); void receive_player_water_sound(const PlayerWaterSound& rsp); void send_player_water_sound(bool underwater, const glm::vec3& pos); @@ -90,8 +72,6 @@ public: void reset_key_status(); std::vector& planes(); const std::vector& render_snapshots() const; - const std::vector& render_player_data() const; - std::vector& render_player_data(); glm::vec3 sunlight_dir() const; bool sphere_collide_world(glm::vec3 center, float radius) const; @@ -99,23 +79,29 @@ public: void request_exit(); bool is_receive_exit(); int chunk_size() const; - static AABB get_block_aabb(const glm::ivec3& pos); + AudioEngine& get_audio(); const AudioEngine& get_audio() const; Config& get_config(); WorldScene& world_scene(); + ClientPlayerManager& player_manager(); + ClientEntityManager& entity_manager(); + std::shared_ptr get_client() const; void set_direct_exit(); void receive_chat_message(ChatMsg& msg); void send_chat_message(ChatMessage& message); void receive_voice_message(VoiceMsg& msg); bool enable_voice_chat() const; + int get_per_tick_time() const override; + + bool is_render(const glm::vec3& pos) const; template - void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) { - m_ticktimers.emplace( - std::piecewise_construct, std::forward_as_tuple(std::string(id)), - std::forward_as_tuple(threshold, std::forward(f))); + void register_timer(std::string_view id, float threshold, Fn&& f) { + m_timers.emplace(std::piecewise_construct, + std::forward_as_tuple(std::string(id)), + std::forward_as_tuple(threshold, std::forward(f))); } private: @@ -136,7 +122,6 @@ private: ChunkPos::TBBHash>; using ChunkPosSet = absl::flat_hash_set; using ChunkPosVector = std::vector; - using OtherPlayerHashMap = std::unordered_map; using chunk_acc = ChunkHashMap::accessor; using chunk_cacc = ChunkHashMap::const_accessor; @@ -147,16 +132,14 @@ private: static constexpr int WORLD_EXIT_TIMEOUT = 200; static constexpr int MAX_UPLOAD_CHUNK_SUM = 16; - ClientPlayer m_player; - OtherPlayerHashMap m_player_info; + std::atomic m_runmode = RunMode::HYBRID; + ClientEntityManager m_entity_manager; + ClientPlayerManager m_player_manager; ChunkHashMap m_chunks; AudioEngine& m_audio; Config& m_config; WorldScene& m_world_scene; std::vector m_planes; - std::jthread m_client_thread; - - mutable std::shared_mutex m_player_info_mutex; tbb::concurrent_queue> m_pending_upload_queue; tbb::concurrent_queue m_dirty_chunk_queue; @@ -166,9 +149,7 @@ private: std::deque m_dirty_queue; std::vector m_render_snapshots; - std::vector m_render_player_data; - tbb::concurrent_unordered_map m_ticktimers; std::unordered_map m_timers; std::atomic m_exit_direct{false}; std::atomic m_game_running{false}; @@ -176,6 +157,7 @@ private: std::atomic m_rendering_distance{24}; std::atomic m_game_ticks{0}; std::atomic m_day_tick{6000}; + std::atomic m_per_tick_time = DEFAULT_PER_TICK_TIME; std::atomic m_requesting_chunk{false}; std::atomic m_is_rebuilding{false}; std::atomic m_chunk_task_id{0}; @@ -187,10 +169,6 @@ private: Random m_random; - void client_run(std::stop_token token); - - void report_player_info(); - void set_block(const glm::ivec3& pos, unsigned id); void update_chunk(const ChunkPosSet& old, const ChunkPosSet& now); diff --git a/include/Cubed/gameplay/creatures/pig.hpp b/include/Cubed/gameplay/creatures/pig.hpp new file mode 100644 index 0000000..fd0f8ce --- /dev/null +++ b/include/Cubed/gameplay/creatures/pig.hpp @@ -0,0 +1,13 @@ +#pragma once + +namespace Cubed { +struct PigTag {}; + +namespace PigDefaults { +constexpr float MAX_SPEED = 0.1f; // tiles/tick → 2 tiles/sec +constexpr float ACCELERATION = 0.01f; // ~10 ticks (0.5s) to reach full speed +constexpr float DECELERATION = 0.02f; // ~5 ticks (0.25s) to stop +constexpr float GRAVITY = 1.0f; // ≈20 tiles/s², close to the player +} // namespace PigDefaults + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/creatures/spawn.hpp b/include/Cubed/gameplay/creatures/spawn.hpp new file mode 100644 index 0000000..e591dcf --- /dev/null +++ b/include/Cubed/gameplay/creatures/spawn.hpp @@ -0,0 +1,20 @@ +#pragma once +#include "Cubed/gameplay/biome.hpp" + +#include +#include +#include +namespace Cubed { +struct SpawnConfig { + std::string_view name; // factory key, e.g. "cubed:pig" + std::span biomes; // allowed biomes + float probability = 0.0f; // per chunk spawn probability + unsigned max_spawn_count = 0; // per chunk_max_spawn_sum +}; + +namespace SpawnDefaults { +constexpr std::array PIG_BIOMES{BiomeType::PLAIN, + BiomeType::FOREST}; +constexpr SpawnConfig PIG{"cubed:pig", PIG_BIOMES, 0.02f, 3}; +} // namespace SpawnDefaults +} // namespace Cubed diff --git a/include/Cubed/gameplay/ecs/ai_struct.hpp b/include/Cubed/gameplay/ecs/ai_struct.hpp new file mode 100644 index 0000000..c9715d8 --- /dev/null +++ b/include/Cubed/gameplay/ecs/ai_struct.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include "Cubed/gameplay/game_time.hpp" +namespace Cubed { +struct AIBase { + TickType interval = 1; + TickType count = 0; +}; + +struct WanderAITag {}; + +struct MoveBoost { + TickType duration = 0; + TickType count = 0; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/animation.hpp b/include/Cubed/gameplay/ecs/animation.hpp new file mode 100644 index 0000000..7a261f3 --- /dev/null +++ b/include/Cubed/gameplay/ecs/animation.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include "Cubed/gameplay/gait.hpp" +namespace Cubed { + +struct WalkPose { + Gait gait = Gait::STOP; + // for arm roll caculate + float walk_time = 0.0f; + // for sound play + float moving_time = 0.0f; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/client_entity.hpp b/include/Cubed/gameplay/ecs/client_entity.hpp new file mode 100644 index 0000000..98df8af --- /dev/null +++ b/include/Cubed/gameplay/ecs/client_entity.hpp @@ -0,0 +1,31 @@ +#pragma once +#include "Cubed/gameplay/ecs/animation.hpp" +#include "Cubed/gameplay/ecs/transform.hpp" +#include "Cubed/gameplay/model.hpp" + +#include +namespace Cubed { +struct BaseClientCreature { + + Transform transform; + + WalkPose pose; + + ModelID model; +}; + +struct SoundTime { + float next_call_time; +}; + +struct ClientEntitySnapshot { + double time_ms = 0.0; + glm::vec3 pos{0.0f}; + glm::vec3 dir{0.0f}; +}; + +struct ClientEntityState { + std::deque history; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/entity.hpp b/include/Cubed/gameplay/ecs/entity.hpp new file mode 100644 index 0000000..67699c4 --- /dev/null +++ b/include/Cubed/gameplay/ecs/entity.hpp @@ -0,0 +1,14 @@ +#pragma once +#include +namespace Cubed { +using EntityID = uint64_t; + +enum class EntityType { CREATURE, ITEM }; + +struct Entity { + EntityID id; + EntityType type; + Entity(EntityID id, EntityType type) : id(id), type(type) {} +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/health.hpp b/include/Cubed/gameplay/ecs/health.hpp new file mode 100644 index 0000000..7d65a76 --- /dev/null +++ b/include/Cubed/gameplay/ecs/health.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace Cubed { + +struct Health { + float hp = 20; + float max_hp = 20; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/identity.hpp b/include/Cubed/gameplay/ecs/identity.hpp new file mode 100644 index 0000000..8982d52 --- /dev/null +++ b/include/Cubed/gameplay/ecs/identity.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace Cubed { +struct EntityInfo { + std::string name; + std::string uuid; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/movement.hpp b/include/Cubed/gameplay/ecs/movement.hpp new file mode 100644 index 0000000..52d605c --- /dev/null +++ b/include/Cubed/gameplay/ecs/movement.hpp @@ -0,0 +1,34 @@ +#pragma once +#include "Cubed/constants.hpp" + +#include +namespace Cubed { +struct TickVelocity { + + glm::vec3 value{0.0f}; + // blocks/tick!!! -1 for in + glm::vec3 max{1.0f, -1.0f, 1.0f}; +}; + +struct Velocity { + + glm::vec3 value{0.0f}; + // blocks/second!!! + glm::vec3 max{4.5f, 7.5f, 7.5f}; +}; + +struct Movement { + + float acceleration = DEFAULT_ACCELERATION; + + float deceleration = DEFAULT_DECELERATION; + + float jump_power = 7.5f; +}; + +struct Gravity { + + float value = DEFAULT_G; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/server_entity.hpp b/include/Cubed/gameplay/ecs/server_entity.hpp new file mode 100644 index 0000000..462e300 --- /dev/null +++ b/include/Cubed/gameplay/ecs/server_entity.hpp @@ -0,0 +1,22 @@ +#pragma once +#include "Cubed/gameplay/ecs/health.hpp" +#include "Cubed/gameplay/ecs/movement.hpp" +#include "Cubed/gameplay/ecs/transform.hpp" +#include "Cubed/gameplay/hitbox.hpp" +namespace Cubed { + +struct BaseServerCreature { + Transform transform{}; + + TickVelocity velocity{}; + + Movement movement{}; + + Gravity gravity{}; + + Health health{}; + + HitboxID hitbox{}; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/state.hpp b/include/Cubed/gameplay/ecs/state.hpp new file mode 100644 index 0000000..4a2e91d --- /dev/null +++ b/include/Cubed/gameplay/ecs/state.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace Cubed { +struct MoveState { + + bool forward = false; + bool back = false; + bool left = false; + bool right = false; + + bool down = false; + bool up = false; + + bool is_fly = false; + bool can_up = true; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/ecs/transform.hpp b/include/Cubed/gameplay/ecs/transform.hpp new file mode 100644 index 0000000..f1554d8 --- /dev/null +++ b/include/Cubed/gameplay/ecs/transform.hpp @@ -0,0 +1,30 @@ +#pragma once +#include +namespace Cubed { +struct Position { + glm::vec3 value{0.0f}; +}; + +struct Orientation { + float yaw = 0.0f; + float pitch = 0.0f; + float roll = 0.0f; +}; + +struct Direction { + glm::vec3 value{0.0f}; +}; + +struct Transform { + Position position{}; + Orientation orientation{}; + Direction direction{}; +}; + +struct RenderTransform { + Position position{}; + Orientation orientation{}; + Direction direction{}; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/player.hpp b/include/Cubed/gameplay/gait.hpp similarity index 96% rename from include/Cubed/gameplay/player.hpp rename to include/Cubed/gameplay/gait.hpp index 09deb0b..da10ea3 100644 --- a/include/Cubed/gameplay/player.hpp +++ b/include/Cubed/gameplay/gait.hpp @@ -1,6 +1,7 @@ #pragma once #include #include + namespace Cubed { enum class Gait { STOP = 0, WALK = 1, RUN = 2 }; constexpr int get_gait_id(Gait gait) { return std::to_underlying(gait); } @@ -17,5 +18,4 @@ inline Gait get_gait_from_id(int id) { throw std::runtime_error("Unknown Gait"); } } - -} // namespace Cubed +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/hitbox.hpp b/include/Cubed/gameplay/hitbox.hpp new file mode 100644 index 0000000..70ab592 --- /dev/null +++ b/include/Cubed/gameplay/hitbox.hpp @@ -0,0 +1,26 @@ +#pragma once +#include + +namespace Cubed { + +using HitboxID = uint32_t; + +struct Hitbox { + glm::vec3 center{0.0f}; + glm::vec3 half{0.0f}; + + Hitbox(glm::vec3 center_point, glm::vec3 half_size) + : center(center_point), half(half_size) {} + + glm::vec3 min() const { return center - half; } + + glm::vec3 max() const { return center + half; } + + bool intersects(const Hitbox& other) const { + return (glm::abs(center.x - other.center.x) <= half.x + other.half.x) && + (glm::abs(center.y - other.center.y) <= half.y + other.half.y) && + (glm::abs(center.z - other.center.z) <= half.z + other.half.z); + } +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/hitbox_manager.hpp b/include/Cubed/gameplay/hitbox_manager.hpp new file mode 100644 index 0000000..b8a2292 --- /dev/null +++ b/include/Cubed/gameplay/hitbox_manager.hpp @@ -0,0 +1,37 @@ +#pragma once +#include "Cubed/gameplay/hitbox.hpp" + +#include +namespace Cubed { +class HitboxManager { +public: + struct Handle { + Hitbox box; + HitboxID id = 0; + }; + HitboxManager(); + ~HitboxManager(); + static HitboxManager& instance(); + + [[nodiscard]] + Handle get_hitbox(const std::string& key); + [[nodiscard]] + Handle get_hitbox(HitboxID id); + [[nodiscard]] + static Handle hitbox(const std::string& name); + [[nodiscard]] + static Handle hitbox(HitboxID id); + HitboxID get_hitbox_id(const std::string& name); + const std::string& get_hitbox_name(HitboxID id); + +private: + using HitboxMap = tbb::concurrent_hash_map; + using IDMap = tbb::concurrent_hash_map; + using NameMap = tbb::concurrent_hash_map; + HitboxID m_next = 0; + IDMap m_id_map; + NameMap m_name_map; + HitboxMap m_hitboxes; + Handle load(std::string_view name); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/item.hpp b/include/Cubed/gameplay/item.hpp new file mode 100644 index 0000000..71da9dc --- /dev/null +++ b/include/Cubed/gameplay/item.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "Cubed/gameplay/block.hpp" + +#include +#include +#include +namespace Cubed { +using ItemID = uint16_t; + +enum class ItemKind { + NONE, + BLOCK, + SPAWN_EGG + +}; + +using ItemProperty = std::variant; + +struct ItemData { + ItemID id = 0; + std::string name; + std::string local_name; + std::string description; + std::string path; + ItemKind kind = ItemKind::NONE; + ItemProperty property; +}; + +inline constexpr ItemKind get_item_kind(std::string_view kind) { + if (kind == "block") { + return ItemKind::BLOCK; + } + if (kind == "spawn_egg") { + return ItemKind::SPAWN_EGG; + } + return ItemKind::NONE; +} + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/item_manager.hpp b/include/Cubed/gameplay/item_manager.hpp new file mode 100644 index 0000000..c9fc02c --- /dev/null +++ b/include/Cubed/gameplay/item_manager.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include "Cubed/gameplay/item.hpp" + +#include +#include +#include +namespace Cubed { +class ItemManager { +public: + ItemManager(); + void init(); + static ItemManager& instance(); + + static const ItemData& get(std::string_view key); + static const ItemData& get(ItemID id); + + static ItemID size(); + + const ItemData& get_item_data(std::string_view key) const; + const ItemData& get_item_data(ItemID id) const; + +private: + using ItemMap = tbb::concurrent_hash_map; + using acc = ItemMap::accessor; + using cacc = ItemMap::const_accessor; + + using IDMap = tbb::concurrent_hash_map; + using BlockToIDMap = tbb::concurrent_hash_map; + void add(const std::filesystem::path& path); + + ItemMap m_map; + + IDMap m_id_map; + BlockToIDMap m_block_to_id_map; + + static inline const ItemData EMPTY; +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/item_stack.hpp b/include/Cubed/gameplay/item_stack.hpp index 5e723a2..70fd02f 100644 --- a/include/Cubed/gameplay/item_stack.hpp +++ b/include/Cubed/gameplay/item_stack.hpp @@ -1,8 +1,9 @@ #pragma once -#include "Cubed/gameplay/block.hpp" +#include "Cubed/gameplay/item.hpp" namespace Cubed { + struct ItemStack { - BlockType type = 0; + ItemID id = 0; size_t sum = 0; }; } // namespace Cubed diff --git a/include/Cubed/gameplay/local_player.hpp b/include/Cubed/gameplay/local_player.hpp new file mode 100644 index 0000000..520c05c --- /dev/null +++ b/include/Cubed/gameplay/local_player.hpp @@ -0,0 +1,161 @@ +#pragma once +#include "Cubed/constants.hpp" +#include "Cubed/gameplay/block.hpp" +#include "Cubed/gameplay/chunk_pos.hpp" +#include "Cubed/gameplay/ecs/animation.hpp" +#include "Cubed/gameplay/ecs/identity.hpp" +#include "Cubed/gameplay/ecs/movement.hpp" +#include "Cubed/gameplay/ecs/state.hpp" +#include "Cubed/gameplay/ecs/transform.hpp" +#include "Cubed/gameplay/game_mode.hpp" +#include "Cubed/gameplay/game_time.hpp" +#include "Cubed/gameplay/hitbox.hpp" +#include "Cubed/gameplay/item_stack.hpp" +#include "Cubed/input/event.hpp" +#include "Cubed/input/input.hpp" + +#include +#include +#include +#include +namespace Cubed { + +class ClientWorld; +class LocalPlayer { +public: + static constexpr size_t HOTBAR_SUM = 10; + static constexpr float WALK_SOUND_INTERVAL = 0.45f; + static constexpr float RUN_SOUND_INTERVAL = 0.3f; + using ChunkPosSet = absl::flat_hash_set; + LocalPlayer(ClientWorld& world); + ~LocalPlayer(); + + bool handle_mouse_button_event(const MouseButtonEvent& e); + bool handle_key_event(const KeyEvent& e); + bool handle_mouse_wheel_event(const MouseWheelEvent& e); + + void update_front_vec(float offset_x, float offset_y); + bool update_player_move_state(Key key, KeyAction action); + bool update_scroll(float yoffset); + + void update_chunk_set(const ChunkPosSet& set); + + const ChunkPosSet& get_chunk_pos_set() const; + ChunkPosSet get_chunk_pos_set(); + + const glm::vec3& get_front() const; + + const std::optional& get_look_block_pos() const; + // thread safe + glm::vec3 get_player_pos() const; + const MoveState& get_move_state() const; + + void change_mode(GameMode mode); + void reload_config(); + void set_player_pos(const glm::vec3& pos); + void update(float delta_time); + + float& max_walk_speed(); + float& max_run_speed(); + float& fly_y_speed(); + + const ItemStack& get_current_itemstack() const; + + GameMode& game_mode(); + + ClientWorld& get_world(); + + void set_uuid(std::string_view uuid); + std::string get_uuid() const; + const std::string& get_name() const; + void reset_input_status(); + void init(std::string_view name); + + bool ray_cast(const glm::vec3& start, const glm::vec3& dir, + glm::ivec3& block_pos, glm::vec3& normal, + float distance = 4.0f); + bool is_underwater() const; + void set_underwater(bool u); + void place_block(float dt); + + int selected_hotbar() const; + void set_hotbar(int pos, const ItemStack& item); + std::span get_hotbar() const; + + glm::vec3& max_speed(); + float& acceleration(); + float& deceleration(); + float& g(); + void set_gait(Gait gait); + float yaw() const; + float pitch() const; + float& roll(); + float& walk_time(); + Gait get_gait() const; + +private: + using enum GameMode; + float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED; + float m_max_run_speed = DEFAULT_MAX_RUN_SPEED; + float m_max_y_speed = 7.5f; + static constexpr float MAX_SPACE_ON_TIME = 0.3f; + static constexpr float PLACE_BLOCK_INTERVAL = 0.2f; + + EntityInfo m_info; + Position m_pos; + WalkPose m_walk_pose; + Velocity m_velocity; + Orientation m_angle; + Movement m_movement; + Gravity m_gravity; + MoveState m_move_state; + Direction m_direction; + HitboxID m_hitbox = 0; + + float m_place_time = PLACE_BLOCK_INTERVAL; + + std::array m_hotbar; + float m_sensitivity = 0.15f; + + float space_on_time = 0.0f; + bool space_on = false; + + int m_selected_hotbar = 0; + + bool m_moving = false; + bool m_sprinting = false; + bool m_underwater = false; + + // player is tow block tall, the pos is the lower pos + ChunkPos m_last_chunk_pos{0, 0}; + + glm::vec3 m_front{0, 0, -1}; + glm::vec3 m_right{0, 0, 0}; + + MouseState m_mouse_state{}; + GameMode m_game_mode = CREATIVE; + std::optional m_look_block = std::nullopt; + std::string m_name{}; + mutable std::shared_mutex m_uuid_mutex; + std::string m_uuid; + ClientWorld& m_world; + + std::unordered_map m_timers; + + mutable std::shared_mutex m_player_pos_mutex; + mutable std::shared_mutex m_chunk_pos_mutex; + ChunkPosSet m_player_chunk_pos_set; + + void update_direction(); + void update_lookup_block(); + void update_move(float dt); + void update_player_chunk(); + + void play_walk_sound(float dt); + Gait compute_gait() const; + + void update_speed(float dt); + std::tuple update_physical(float dt, glm::vec3& pos); + glm::vec3 get_move_distance(float dt); +}; +} // namespace Cubed diff --git a/include/Cubed/gameplay/model.hpp b/include/Cubed/gameplay/model.hpp new file mode 100644 index 0000000..3899181 --- /dev/null +++ b/include/Cubed/gameplay/model.hpp @@ -0,0 +1,7 @@ +#pragma once +#include + +using ModelID = uint32_t; +struct Model { + ModelID id; +}; \ No newline at end of file diff --git a/include/Cubed/gameplay/network_server.hpp b/include/Cubed/gameplay/network_server.hpp index f12fbd6..7abf197 100644 --- a/include/Cubed/gameplay/network_server.hpp +++ b/include/Cubed/gameplay/network_server.hpp @@ -14,8 +14,7 @@ public: void stop(); // Run in another thread after initialization is complete - void start_server(int port); - void start_server(); + void start_server(int port, RunMode mode); int port() const; ServerWorld& server_world(); diff --git a/include/Cubed/gameplay/packet.hpp b/include/Cubed/gameplay/packet.hpp index 1af675a..a2a3ac7 100644 --- a/include/Cubed/gameplay/packet.hpp +++ b/include/Cubed/gameplay/packet.hpp @@ -51,18 +51,28 @@ enum class PacketEnum : uint16_t { LOGIN_RSP = 1002, LOGOUT_REQ = 1003, LOGOUT_RSP = 1004, + PLAYER_INFO = 2001, C2S_PLAYER_INFO = 2002, PLAYER_INFO_RSP = 2003, PLAYER_WATER_SOUND = 2004, + CHUNK_DATA_REQ = 3001, CHUNK_DATA_RSP = 3002, BLOCK_CHANGE_REQ = 3003, BLOCK_CHANGE_RSP = 3004, S2C_CLEAR_ALL_CHUNKS = 3005, UPDATE_TIME = 3006, + S2C_ENTITY_CREATE = 3007, + S2C_ENTITY_DESTORY = 3008, + C2S_ENTITY_CREATE_REQUEST = 3009, + C2S_ENTITY_DESTORY_REQUEST = 3010, + S2C_ENTITY_UPDATE = 3011, + S2C_ENTITY_UPDATE_BATCH = 3012, + CHAT_MSG = 4001, VOICE_MSG = 4002, + PING = 9001, PONG = 9002 @@ -111,6 +121,18 @@ template <> constexpr uint16_t get_packet_id() { template <> constexpr uint16_t get_packet_id() { return std::to_underlying(PacketEnum::S2C_CLEAR_ALL_CHUNKS); } +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::S2C_ENTITY_CREATE); +} +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::S2C_ENTITY_DESTORY); +} +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::C2S_ENTITY_CREATE_REQUEST); +} +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::C2S_ENTITY_DESTORY_REQUEST); +} template <> constexpr uint16_t get_packet_id() { return std::to_underlying(PacketEnum::UPDATE_TIME); } @@ -129,6 +151,12 @@ template <> constexpr uint16_t get_packet_id() { template <> constexpr uint16_t get_packet_id() { return std::to_underlying(PacketEnum::VOICE_MSG); } +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::S2C_ENTITY_UPDATE); +} +template <> constexpr uint16_t get_packet_id() { + return std::to_underlying(PacketEnum::S2C_ENTITY_UPDATE_BATCH); +} template requires std::derived_from @@ -180,6 +208,12 @@ Packet make_packet(const T& msg) { return packet; } +template + requires std::derived_from +Packet make_packet(const T* msg) { + return make_packet(*msg); +} + inline PacketHeader decode_packet_header(std::span header) { if (header.size() < HEADER_LEN) throw std::runtime_error("Invalid header"); diff --git a/include/Cubed/gameplay/server_chunk.hpp b/include/Cubed/gameplay/server_chunk.hpp index 8fa7075..9eb9a9a 100644 --- a/include/Cubed/gameplay/server_chunk.hpp +++ b/include/Cubed/gameplay/server_chunk.hpp @@ -2,6 +2,7 @@ #include "Cubed/constants.hpp" #include "Cubed/gameplay/biome.hpp" #include "Cubed/gameplay/block.hpp" +#include "Cubed/gameplay/chunk.hpp" #include "Cubed/gameplay/chunk_generator.hpp" #include "Cubed/gameplay/chunk_pos.hpp" @@ -11,7 +12,7 @@ #include namespace Cubed { class ServerWorld; -class ServerChunk { +class ServerChunk : public Chunk { public: ServerChunk(ServerWorld& world, ChunkPos chunk_pos, bool temp_chunk = false); diff --git a/include/Cubed/gameplay/server_entity_manager.hpp b/include/Cubed/gameplay/server_entity_manager.hpp new file mode 100644 index 0000000..e5af786 --- /dev/null +++ b/include/Cubed/gameplay/server_entity_manager.hpp @@ -0,0 +1,80 @@ +#pragma once +#include "Cubed/gameplay/ecs/entity.hpp" +#include "Cubed/gameplay/gait.hpp" +#include "glm/ext/vector_float3.hpp" + +#include +#include +#include +#include +namespace Cubed { +class ServerWorld; +class Session; +class ServerEntityManager { +public: + static constexpr size_t PER_CREATURE_LIMITS = 100; + + ServerEntityManager(ServerWorld& world); + + void init(); + void update(); + void add_creature(std::string_view name, const glm::vec3& world_pos); + void destory(EntityID id); + void handle_player_login(std::shared_ptr session); + + size_t max_creature_sum() const; + size_t creature_sum() const; + size_t entity_sum() const; + +private: + enum class Command { CREATE, SEND_ALL_ENTITIES, DESTORY }; + struct EntityCreateElement { + std::string name; + glm::vec3 pos; + }; + + struct EntitySendData { + EntityID id; + glm::vec3 pos; + glm::vec3 dir; + Gait gait; + }; + + using EntityMap = tbb::concurrent_hash_map; + using acc = EntityMap::accessor; + using cacc = EntityMap::const_accessor; + using CreateFunc = std::function; + using TaskElement = + std::variant, EntityCreateElement, EntityID>; + using TaskPair = std::pair; + ServerWorld& m_world; + std::atomic m_creature_sum{0}; + std::atomic m_entity_sum{0}; + tbb::concurrent_queue m_tasks; + entt::registry m_registry; + EntityID m_next = 0; + EntityMap m_entities; + std::unordered_map m_factories; + void create_entity(std::string_view name, const glm::vec3& pos); + void handle_entity_create(EntityID id, std::string_view name, + const glm::vec3& pos); + void handle_entity_destory(EntityID id); + void handle_task(); + void send_all_entities(std::shared_ptr& session); + void update_ai(entt::entity e); + void update_move(entt::entity e); + void update_send(entt::entity e, + tbb::concurrent_vector& sessions); + template + EntityID create_entity_in_factory(Args&&... args) { + auto entity = m_registry.create(); + + ((m_registry.emplace>( + entity, std::forward(args))), + ...); + auto id = m_next++; + m_entities.emplace(id, entity); + return id; + } +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/server_player.hpp b/include/Cubed/gameplay/server_player.hpp index c98ee92..9c28b79 100644 --- a/include/Cubed/gameplay/server_player.hpp +++ b/include/Cubed/gameplay/server_player.hpp @@ -1,7 +1,7 @@ #pragma once #include "Cubed/gameplay/chunk_pos.hpp" +#include "Cubed/gameplay/gait.hpp" #include "Cubed/gameplay/game_time.hpp" -#include "Cubed/gameplay/player.hpp" #include #include diff --git a/include/Cubed/gameplay/server_world.hpp b/include/Cubed/gameplay/server_world.hpp index 8502570..e1e6930 100644 --- a/include/Cubed/gameplay/server_world.hpp +++ b/include/Cubed/gameplay/server_world.hpp @@ -7,7 +7,9 @@ #include "Cubed/gameplay/packet.hpp" // IWYU pragma: keep #include "Cubed/gameplay/river_worm.hpp" #include "Cubed/gameplay/server_chunk.hpp" +#include "Cubed/gameplay/server_entity_manager.hpp" #include "Cubed/gameplay/server_player.hpp" +#include "Cubed/gameplay/world.hpp" #include "Cubed/tools/priority_thread_pool.hpp" #include "Cubed/tools/recent_queue.hpp" #include "Cubed/tools/sensitive_filter.hpp" @@ -20,19 +22,20 @@ #include #include #include +#include #include #include #include namespace Cubed { class Session; -class ServerWorld { +class ServerWorld : public World { public: enum class ThreadPoolKind { NET, GEN }; ServerWorld(Config& config); ~ServerWorld(); void stop(); void handle_player_exit(const std::string& uuid); - void init_world(); + void init_world(RunMode mode); void need_gen(std::string uuid); void update(); void hot_reload(); @@ -84,7 +87,24 @@ public: void handle_chat_message(ChatMsg& msg); void handle_voice_message(VoiceMsg& msg); + + void handle_entity_create(C2SEntityCreateRequest& req); + void handle_entity_destory(C2SEntityDestoryRequest& req); + int chunk_size() const; + + tbb::concurrent_vector> get_all_session() const; + + uint32_t get_chunk_ref_count(const glm::vec3& pos) const; + ServerEntityManager& entity_manager(); + std::shared_ptr get_compute_pool(); + size_t player_sum() const; + + int get_block(const glm::ivec3& block_pos) const override; + bool is_solid(const glm::ivec3& block_pos) const override; + bool can_pass_block(const glm::ivec3& block_pos) const override; + BlockType get_block_tpye(const glm::ivec3& block_pos) const override; + int get_per_tick_time() const override; template void register_timer(std::string_view id, TickType threshold, Fn&& f) { m_timers.emplace(std::piecewise_construct, @@ -97,7 +117,27 @@ private: struct ChunkEntity { ChunkState state; std::shared_ptr chunk; - uint32_t ref_count = 0; + std::atomic ref_count = 0; + ChunkEntity() = default; + ChunkEntity(ChunkState s, std::shared_ptr c = {}) + : state(s), chunk(std::move(c)) {} + + ChunkEntity& operator=(ChunkEntity&& o) noexcept { + if (this == &o) { + return *this; + } + + state = std::exchange(o.state, ServerWorld::ChunkState::NONE); + chunk = std::move(o.chunk); + ref_count = o.ref_count.exchange(0); + return *this; + } + + ChunkEntity(ChunkEntity&& o) noexcept + : state(std::exchange(o.state, ServerWorld::ChunkState::NONE)), + chunk(std::move(o.chunk)), ref_count(o.ref_count.exchange(0)) {} + ChunkEntity(const ChunkEntity&) = delete; + ChunkEntity& operator=(const ChunkEntity&) = delete; }; enum class ChunkLoadStyle { RANDOM, CENTER }; @@ -119,13 +159,14 @@ private: using PlayerUUIDMap = tbb::concurrent_hash_map; using chunk_acc = ChunkHashMap::accessor; - using chunk_caac = ChunkHashMap::const_accessor; + using chunk_cacc = ChunkHashMap::const_accessor; using uuid_acc = PlayerUUIDMap::accessor; using uuid_cacc = PlayerUUIDMap::const_accessor; Config& m_config; - + std::atomic m_runmode{RunMode::HYBRID}; + ServerEntityManager m_entity_manager; // key = uuid PlayerHashMap m_players; ChunkHashMap m_chunks; @@ -146,8 +187,9 @@ private: std::atomic m_init{false}; std::atomic m_stopped{false}; std::atomic m_rendering_distance{24}; - std::atomic m_gen_pool_threads{0}; - std::atomic m_net_pool_threads{0}; + std::atomic m_gen_threads{0}; + std::atomic m_net_threads{0}; + std::atomic m_compute_threads{0}; std::atomic m_max_threads{1}; std::atomic m_player_sum{0}; std::atomic m_game_ticks{0}; @@ -155,7 +197,7 @@ private: std::atomic m_tick_running{true}; std::atomic m_per_tick_time = DEFAULT_PER_TICK_TIME; // ms - mutable std::shared_mutex m_player_mutex; + mutable std::shared_mutex m_players_mutex; std::mutex m_need_gen_queue_mutex; std::condition_variable_any m_gen_cv; @@ -163,6 +205,7 @@ private: std::atomic> m_gen_thread_pool; std::atomic> m_net_thread_pool; + std::atomic> m_compute_thread_pool; std::atomic m_chunk_load_style{ChunkLoadStyle::CENTER}; diff --git a/include/Cubed/gameplay/systems/physical_system.hpp b/include/Cubed/gameplay/systems/physical_system.hpp new file mode 100644 index 0000000..5a57406 --- /dev/null +++ b/include/Cubed/gameplay/systems/physical_system.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "Cubed/gameplay/ecs/movement.hpp" + +#include +namespace Cubed { +class ServerWorld; +class PhysicalSystem { +public: + static glm::vec3 get_move_distance(const TickVelocity& v); + + static void update(ServerWorld& world, entt::registry& registry, + entt::entity e); + +private: +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/systems/speed_system.hpp b/include/Cubed/gameplay/systems/speed_system.hpp new file mode 100644 index 0000000..090b5e4 --- /dev/null +++ b/include/Cubed/gameplay/systems/speed_system.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include +namespace Cubed { +class SpeedSystem { +public: + static void update(float dt, entt::registry& registry, entt::entity e); + +private: +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/systems/wander_ai_system.hpp b/include/Cubed/gameplay/systems/wander_ai_system.hpp new file mode 100644 index 0000000..24c7f12 --- /dev/null +++ b/include/Cubed/gameplay/systems/wander_ai_system.hpp @@ -0,0 +1,14 @@ +#pragma once +#include "Cubed/gameplay/ecs/ai_struct.hpp" +#include "Cubed/gameplay/ecs/server_entity.hpp" + +#include +namespace Cubed { +class WanderAISystem { +public: + static void update(entt::registry& registry, entt::entity e); + +private: + static void do_ai(BaseServerCreature& creature, MoveBoost& move_boost); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/gameplay/world.hpp b/include/Cubed/gameplay/world.hpp new file mode 100644 index 0000000..710166a --- /dev/null +++ b/include/Cubed/gameplay/world.hpp @@ -0,0 +1,32 @@ +#pragma once +#include "Cubed/gameplay/block.hpp" +#include "Cubed/gameplay/hitbox.hpp" + +#include +namespace Cubed { +class World { +public: + World() = default; + World(const World&) = delete; + World(World&&) = delete; + World& operator=(const World&) = delete; + World& operator=(World&&) = delete; + virtual ~World() = default; + + virtual int get_block(const glm::ivec3& block_pos) const = 0; + virtual bool is_solid(const glm::ivec3& block_pos) const = 0; + virtual bool can_pass_block(const glm::ivec3& block_pos) const = 0; + virtual BlockType get_block_tpye(const glm::ivec3& block_pos) const = 0; + virtual int get_per_tick_time() const = 0; + + static Hitbox get_block_aabb(const glm::ivec3& pos) { + return {glm::vec3{static_cast(pos.x) + 0.5f, + static_cast(pos.y) + 0.5f, + static_cast(pos.z) + 0.5f}, + glm::vec3{0.5f, 0.5f, 0.5f}}; + } +}; + +enum class RunMode { CLIENT_ONLY, SERVER_ONLY, HYBRID }; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/input/input.hpp b/include/Cubed/input/input.hpp index ef3e549..f634796 100644 --- a/include/Cubed/input/input.hpp +++ b/include/Cubed/input/input.hpp @@ -2,15 +2,6 @@ namespace Cubed { -struct MoveState { - bool forward = false; - bool back = false; - bool left = false; - bool right = false; - bool down = false; - bool up = false; -}; - struct MouseState { bool left = false; bool right = false; diff --git a/include/Cubed/render/model_manager.hpp b/include/Cubed/render/model_manager.hpp new file mode 100644 index 0000000..c1109f1 --- /dev/null +++ b/include/Cubed/render/model_manager.hpp @@ -0,0 +1,46 @@ +#pragma once +#include "Cubed/gameplay/model.hpp" +#include "Cubed/render/model_node.hpp" +#include "Cubed/tools/model_loader.hpp" + +#include +namespace Cubed { +class ModelManager { +public: + struct Handle { + const ModelNode& node; + ModelID id = 0; + }; + + ModelManager(); + ModelManager(const ModelManager&) = delete; + ModelManager(ModelManager&&) = delete; + ModelManager& operator=(const ModelManager&) = delete; + ModelManager& operator=(ModelManager&&) = delete; + static ModelManager& instance(); + ~ModelManager(); + [[nodiscard]] + Handle get_model(const std::string& model_name); + [[nodiscard]] + Handle get_model(ModelID id); + [[nodiscard]] + static Handle model(const std::string& model_name); + [[nodiscard]] + static Handle model(ModelID id); + ModelID get_model_id(const std::string& name); + const std::string& get_model_name(ModelID id); + void init(); + +private: + ModelLoader m_loader; + ModelID m_next = 0; + using ModelMap = tbb::concurrent_hash_map; + using IDMap = tbb::concurrent_hash_map; + using NameMap = tbb::concurrent_hash_map; + ModelMap m_models; + IDMap m_id_map; + NameMap m_name_map; + Handle load_model(std::string_view model_name); + void load_anim_config(ModelNode& node, const std::string& path); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/model_node.hpp b/include/Cubed/render/model_node.hpp new file mode 100644 index 0000000..9f3fac1 --- /dev/null +++ b/include/Cubed/render/model_node.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include "Cubed/primitive_data.hpp" +#include "Cubed/render/texture.hpp" +#include "Cubed/render/vertex_array.hpp" +#include "Cubed/render/vertex_buffer.hpp" + +#include +#include +#include +#include +#include +namespace Cubed { + +struct Mesh { + std::vector vertices; + std::vector indices; + std::unique_ptr vbo; + std::unique_ptr ebo; + std::unique_ptr vao; + std::unique_ptr texture; + void upload() { + vao = std::make_unique(); + vao->bind(); + vbo = std::make_unique(); + vbo->buffer_data(vertices.data(), vertices.size() * sizeof(Vertex3D)); + ebo = std::make_unique(BufferType::ELEMENT_ARRAY_BUFFER); + ebo->buffer_data(indices.data(), indices.size() * sizeof(uint32_t)); + vao->attribute(0, 3, GL_FLOAT, sizeof(Vertex3D), (void*)0); + vao->attribute(1, 2, GL_FLOAT, sizeof(Vertex3D), + (void*)offsetof(Vertex3D, s)); + vao->attribute(2, 3, GL_FLOAT, sizeof(Vertex3D), + (void*)offsetof(Vertex3D, nx)); + }; +}; +struct NodeAnimRule { + enum class Role { NONE, LEG, HEAD }; + std::string node; + Role role = Role::NONE; + float phase = 0.0f; +}; + +struct ModelAnimConfig { + float walk_speed = 6.0f; + float walk_amp = 25.0f; + float run_speed = 12.0f; + float run_amp = 40.0f; + float body_bob = 0.05f; + float head_amp = 4.0f; + std::vector nodes; + + const NodeAnimRule* rule_for(std::string_view name) const { + for (const auto& r : nodes) { + if (r.node == name) { + return &r; + } + } + return nullptr; + } + + bool has_role(NodeAnimRule::Role role) const { + for (const auto& r : nodes) { + if (r.role == role) { + return true; + } + } + return false; + } +}; +struct ModelNode { + std::string name; + glm::mat4 transform{1.0f}; + + std::vector meshes; + std::vector children; + ModelAnimConfig anim; +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/model_renderer.hpp b/include/Cubed/render/model_renderer.hpp new file mode 100644 index 0000000..53c852a --- /dev/null +++ b/include/Cubed/render/model_renderer.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include "Cubed/gameplay/ecs/animation.hpp" +#include "Cubed/gameplay/model.hpp" +#include "Cubed/render/model_node.hpp" + +#include +#include +#include +#include +namespace Cubed { +class Renderer; +class Camera; +class ModelRender { +public: + struct InstanceData { + glm::vec3 pos{0.0f}; + float yaw = 0.0f; + WalkPose pose; + }; + + struct DrawEntry { + const Mesh* mesh = nullptr; + size_t node_slot = 0; + }; + + struct ModelBatch { + size_t node_count = 0; + size_t capacity = 0; + std::vector entries; + std::vector instance_matrices; + std::unique_ptr instance_vbo; + }; + + ModelRender(Renderer& renderer); + + void render_instance(ModelID id, size_t sum, const Camera& camera, + bool shadow); + void build_vertices(ModelID id, std::span instances); + +private: + Renderer& m_renderer; + + std::unordered_map m_batches; + + size_t collect_matrices(const ModelNode& node, const glm::mat4& parent, + const WalkPose& pose, const ModelAnimConfig& cfg, + std::vector& out, size_t slot); + glm::mat4 pose_node(const ModelNode& node, const ModelAnimConfig& cfg, + const WalkPose& pose); + + ModelBatch& get_batch(ModelID id, const ModelNode& root); + size_t flatten_nodes(const ModelNode& node, size_t slot, ModelBatch& batch); +}; +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/render/player_renderer.hpp b/include/Cubed/render/player_renderer.hpp index 4ac66ad..d35e80e 100644 --- a/include/Cubed/render/player_renderer.hpp +++ b/include/Cubed/render/player_renderer.hpp @@ -13,9 +13,7 @@ public: PlayerRenderer(Renderer& renderer); ~PlayerRenderer(); void init(); - void render(const Shader& shader, ClientWorld& world); - void shadow_render(const Shader& shader, glm::mat4& light_matrix, - ClientWorld& world); + void render(const Shader& shader, ClientWorld& world, bool shadow_render); private: struct PlayerVertex { diff --git a/include/Cubed/render/renderer.hpp b/include/Cubed/render/renderer.hpp index 00edc28..b2deb3f 100644 --- a/include/Cubed/render/renderer.hpp +++ b/include/Cubed/render/renderer.hpp @@ -4,6 +4,7 @@ #include "Cubed/constants.hpp" #include "Cubed/input/event.hpp" #include "Cubed/primitive_data.hpp" +#include "Cubed/render/model_renderer.hpp" #include "Cubed/render/player_renderer.hpp" #include "Cubed/render/shader_manager.hpp" #include "Cubed/render/vertex_array.hpp" @@ -19,6 +20,7 @@ namespace Cubed { class TextureManager; class ClientWorld; +class ModelManager; class DevPanel; class Renderer { public: @@ -79,9 +81,10 @@ public: bool handle_event(const Event& e); + ModelRender& model_renderer(); + private: TextureManager& m_texture_manager; - bool m_init = false; float m_aspect = 0.0f; @@ -118,6 +121,7 @@ private: std::vector m_ui; WorldRenderer m_world_renderer; + ModelRender m_model_renderer; Config& m_config; bool handle_window_resize_event(const WindowResizeEvent& e); diff --git a/include/Cubed/render/texture.hpp b/include/Cubed/render/texture.hpp index f058ef5..129a400 100644 --- a/include/Cubed/render/texture.hpp +++ b/include/Cubed/render/texture.hpp @@ -38,9 +38,11 @@ enum TextureFormat : GLenum { R8 = GL_R8, RGB = GL_RGB, RGBA8 = GL_RGBA8, + BGRA = GL_BGRA }; - +// You need to set the texture scaling method, otherwise it will render as +// black. class Texture { public: explicit Texture(TextureType type); diff --git a/include/Cubed/render/vertex_array.hpp b/include/Cubed/render/vertex_array.hpp index 8e55da1..cc5442c 100644 --- a/include/Cubed/render/vertex_array.hpp +++ b/include/Cubed/render/vertex_array.hpp @@ -19,6 +19,7 @@ public: void attribute(GLuint index, GLint size, GLenum type, GLsizei stride, const void* ptr, bool normalized = false) const; + void divisor(GLuint index, GLuint divisor = 1); private: GLuint m_vao = 0; diff --git a/include/Cubed/render/vertex_buffer.hpp b/include/Cubed/render/vertex_buffer.hpp index 83b9380..ca32817 100644 --- a/include/Cubed/render/vertex_buffer.hpp +++ b/include/Cubed/render/vertex_buffer.hpp @@ -26,6 +26,8 @@ public: GLuint id() const; void buffer_data(const void* data, GLsizeiptr size, BufferUsage usage = BufferUsage::STATIC_DRAW) const; + void buffer_sub_data(const void* data, GLsizeiptr size, + GLintptr offset) const; private: GLuint m_vbo = 0; diff --git a/include/Cubed/render/world_renderer.hpp b/include/Cubed/render/world_renderer.hpp index 40cd118..51a2c1f 100644 --- a/include/Cubed/render/world_renderer.hpp +++ b/include/Cubed/render/world_renderer.hpp @@ -1,10 +1,14 @@ #pragma once +#include "Cubed/gameplay/model.hpp" #include "Cubed/render/frame_buffer.hpp" +#include "Cubed/render/model_renderer.hpp" #include "Cubed/render/player_renderer.hpp" #include "Cubed/render/texture.hpp" #include #include +#include +#include namespace Cubed { class Renderer; class ClientWorld; @@ -12,6 +16,8 @@ class TextureManager; class Camera; class WorldRenderer { public: + using InstanceDataMap = + std::unordered_map>; struct ParallelLight { glm::vec3 sundir; // direction from sun to vertex glm::vec3 lightdir; @@ -128,11 +134,13 @@ private: void render_world(ClientWorld& world); - void shadow_map_generate(ClientWorld& world); + void shadow_map_generate(ClientWorld& world, const InstanceDataMap& map); void render_underwater(ClientWorld& world); void render_outline(ClientWorld& world); - void render_player(ClientWorld& world); + void shadow_entity(ClientWorld& world, const glm::mat4& light_matrix, + const InstanceDataMap& map); + void render_entity(ClientWorld& world, const InstanceDataMap& map); void render_normal_block(const glm::mat4& model_mat, const glm::mat4& mv_mat, const glm::mat4& norm_mat, @@ -146,5 +154,6 @@ private: float angle_step_deg) const; glm::vec3 get_smoothed_shadow_lightdir(const glm::vec3& raw_shadow_sundir, float dt); + InstanceDataMap entity_build(ClientWorld& world); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/scene/scene_manager.hpp b/include/Cubed/scene/scene_manager.hpp index 973d380..e5a322c 100644 --- a/include/Cubed/scene/scene_manager.hpp +++ b/include/Cubed/scene/scene_manager.hpp @@ -36,6 +36,7 @@ public: App& app(); WorldSceneParam& world_scene_param(); + void push(SceneType type); private: enum class OperationType { PUSH, POP, CHANGE }; @@ -51,7 +52,7 @@ private: std::stack> m_scenes; void process_operation(); void change(SceneType type); - void push(SceneType type); + void pop(bool re_enter = true); std::unique_ptr create_scene(SceneType); diff --git a/include/Cubed/scene/world_scene.hpp b/include/Cubed/scene/world_scene.hpp index 6878d4b..448142e 100644 --- a/include/Cubed/scene/world_scene.hpp +++ b/include/Cubed/scene/world_scene.hpp @@ -42,6 +42,8 @@ public: bool is_recording() const; + RunMode runmode() const; + private: enum class PauseUI { PAUSE_MENU, INVENTORY }; SceneManager& m_scene_manager; @@ -61,6 +63,7 @@ private: ErrorUI m_error_ui; const Argument& m_argument; VoiceInputType m_input_type; + RunMode m_runmode = RunMode::HYBRID; bool handle_mouse_move_event(const MouseMoveEvent& e) override; bool handle_mouse_button_event(const MouseButtonEvent& e) override; bool handle_window_resize_event(const WindowResizeEvent& e) override; diff --git a/include/Cubed/texture_manager.hpp b/include/Cubed/texture_manager.hpp index 998ffd7..b078106 100644 --- a/include/Cubed/texture_manager.hpp +++ b/include/Cubed/texture_manager.hpp @@ -1,6 +1,7 @@ #pragma once #include "Cubed/config.hpp" #include "Cubed/gameplay/block.hpp" +#include "Cubed/gameplay/item.hpp" #include "Cubed/input/event.hpp" #include "Cubed/render/texture.hpp" @@ -10,6 +11,26 @@ namespace Cubed { class TextureManager { +public: + using ItemTextureMap = std::unordered_map>; + TextureManager(Config& config); + ~TextureManager(); + + void delete_texture(); + const Texture* get_block_status_array() const; + const Texture* get_texture_array() const; + const Texture* get_cross_plane_array() const; + const Texture* get_image_texture(const std::string& path); + const Texture* get_pbr_texture() const; + const ItemTextureMap& get_item_textures() const; + const Texture* get_skin() const; + void init_texture(); + + void need_reload(); + void update(); + int max_aniso() const; + bool handle_event(const Event& e); + private: bool m_need_reload = false; bool m_init = false; @@ -18,7 +39,7 @@ private: std::unique_ptr m_cross_plane_array; std::unique_ptr m_normal_texture_array; std::unique_ptr m_skin; - std::vector> m_item_textures; + ItemTextureMap m_item_textures; std::unordered_map> m_ui_map; GLfloat m_max_aniso = 0.0f; Config& m_config; @@ -26,7 +47,7 @@ private: void load_block_status(unsigned status_id); void load_block_texture(unsigned block_id); - void load_block_item_texture(unsigned id); + void init_item_texture(); void load_cross_plane_texture(unsigned id); const Texture* load_image_texture(const std::string& path); void load_pbr_texture(unsigned id); @@ -37,25 +58,6 @@ private: void init_skin(); void hot_reload(); bool handle_key_event(const KeyEvent& e); - -public: - TextureManager(Config& config); - ~TextureManager(); - - void delete_texture(); - const Texture* get_block_status_array() const; - const Texture* get_texture_array() const; - const Texture* get_cross_plane_array() const; - const Texture* get_image_texture(const std::string& path); - const Texture* get_pbr_texture() const; - const std::vector>& get_item_textures() const; - const Texture* get_skin() const; - void init_texture(); - - void need_reload(); - void update(); - int max_aniso() const; - bool handle_event(const Event& e); }; } // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/cubed_concepts.hpp b/include/Cubed/tools/cubed_concepts.hpp new file mode 100644 index 0000000..7e8c846 --- /dev/null +++ b/include/Cubed/tools/cubed_concepts.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include +namespace Cubed { +template +concept Ptr = std::is_pointer_v; +} diff --git a/include/Cubed/tools/cubed_random.hpp b/include/Cubed/tools/cubed_random.hpp index 8e13065..7553dba 100644 --- a/include/Cubed/tools/cubed_random.hpp +++ b/include/Cubed/tools/cubed_random.hpp @@ -1,4 +1,6 @@ #pragma once +#include "glm/ext/vector_float3.hpp" + #include namespace Cubed { @@ -14,6 +16,8 @@ public: int random_int(int min, int max); float random_float(float min, float max); + glm::vec3 random_direction_horizontal(); + private: unsigned int m_seed = 0; std::mt19937 m_engine; diff --git a/include/Cubed/tools/json_utils.hpp b/include/Cubed/tools/json_utils.hpp new file mode 100644 index 0000000..043742c --- /dev/null +++ b/include/Cubed/tools/json_utils.hpp @@ -0,0 +1,8 @@ + +#include +#include +#include +namespace Tools { +std::unordered_map +doc_to_map(const rapidjson::Document& doc); +} \ No newline at end of file diff --git a/include/Cubed/tools/model_loader.hpp b/include/Cubed/tools/model_loader.hpp new file mode 100644 index 0000000..da301da --- /dev/null +++ b/include/Cubed/tools/model_loader.hpp @@ -0,0 +1,22 @@ +#pragma once +#include "Cubed/render/model_node.hpp" + +#include +#include +namespace Cubed { + +class ModelLoader { +public: + ModelLoader(); + ModelNode load(const std::string& path); + +private: + Assimp::Importer m_importer; + ModelNode process_node(aiNode* node, const aiScene* scene); + Mesh process_mesh(aiMesh* mesh, const aiScene* scene); + bool process_texture(Mesh& mesh, aiMaterial* material, const aiScene* scene, + aiTextureType type); + glm::mat4 convert_matrix(const aiMatrix4x4& matrix); +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/name_space.hpp b/include/Cubed/tools/name_space.hpp new file mode 100644 index 0000000..10c9d50 --- /dev/null +++ b/include/Cubed/tools/name_space.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +namespace Cubed { +inline std::vector parse_namespace(std::string_view str) { + std::vector space; + space.reserve(4); + std::size_t p = str.find(':'); + std::size_t start = 0; + while (p != std::string_view::npos) { + space.emplace_back(str.substr(start, p)); + start = p + 1; + p = str.find(':', p + 1); + } + space.emplace_back(str.substr(start)); + return space; +} +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/net_utils.hpp b/include/Cubed/tools/net_utils.hpp new file mode 100644 index 0000000..56c524e --- /dev/null +++ b/include/Cubed/tools/net_utils.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "Cubed/tools/cubed_concepts.hpp" +#include "common/vector3.pb.h" +#include "glm/ext/vector_float3.hpp" +namespace Cubed { +namespace Tools { +inline void set_net_vec3(Vec3* p, const glm::vec3& pos) { + p->set_x(pos.x); + p->set_y(pos.y); + p->set_z(pos.z); +} +template void set_net_pos(T ptr, const glm::vec3& pos) { + set_net_vec3(ptr->mutable_pos(), pos); +} + +inline glm::vec3 get_net_vec3(const Vec3* p) { + return glm::vec3{p->x(), p->y(), p->z()}; +} +inline glm::vec3 get_net_vec3(const Vec3& p) { + return glm::vec3{p.x(), p.y(), p.z()}; +} +} // namespace Tools +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/sensitive_filter.hpp b/include/Cubed/tools/sensitive_filter.hpp index a980921..bf93e9f 100644 --- a/include/Cubed/tools/sensitive_filter.hpp +++ b/include/Cubed/tools/sensitive_filter.hpp @@ -1,11 +1,13 @@ #pragma once -#include +#include +#include +#include namespace Cubed { class SensitiveFilter { public: SensitiveFilter(); - void load(const nlohmann::json& j); + void load(const rapidjson::Document& doc); std::string filter(std::string_view text); private: diff --git a/include/Cubed/tools/shader_tools.hpp b/include/Cubed/tools/shader_tools.hpp index 443328a..d9eb81e 100644 --- a/include/Cubed/tools/shader_tools.hpp +++ b/include/Cubed/tools/shader_tools.hpp @@ -47,7 +47,7 @@ bool check_opengl_error(); std::string read_shader_source(const std::string& file_path); ImageData load_image_data(const std::string& tex_image_path, - bool check_exist = true); + bool check_exist = true, bool full_path = false); } // namespace Tools diff --git a/include/Cubed/tools/sparse_vector.hpp b/include/Cubed/tools/sparse_vector.hpp new file mode 100644 index 0000000..3b57c61 --- /dev/null +++ b/include/Cubed/tools/sparse_vector.hpp @@ -0,0 +1,318 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +namespace Cubed { +template class SparseVector { +private: + std::vector> m_data; + std::vector m_free_list; + std::vector m_generation; + + std::vector m_dense; + std::vector m_dense_index; + +public: + struct Handle { + uint32_t index; + uint32_t generation; + + uint64_t value() const noexcept { + return (uint64_t(index) << 32) | generation; + } + + bool operator==(const Handle&) const = default; + + struct Hash { + size_t operator()(const Handle& h) const noexcept { + return h.value(); + } + }; + }; + + using value_type = T; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + + using size_type = size_t; + + class iterator { // NOLINT + private: + SparseVector* m_owner; + size_t m_index; + + public: + using iterator_category = std::random_access_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using reference = T&; + + iterator(SparseVector* owner, size_t index) + : m_owner(owner), m_index(index) {} + + reference operator*() { + return m_owner->m_data[m_owner->m_dense[m_index]].value(); + } + + pointer operator->() { return &(**this); } + + iterator& operator++() { + ++m_index; + return *this; + } + + iterator operator++(int) { + iterator tmp = *this; + ++(*this); + return tmp; + } + + iterator& operator--() { + --m_index; + return *this; + } + + iterator operator--(int) { + iterator tmp = *this; + --(*this); + return tmp; + } + + bool operator==(const iterator& other) const { + return m_owner == other.m_owner && m_index == other.m_index; + } + + bool operator!=(const iterator& other) const { + return !(*this == other); + } + + iterator operator+(difference_type n) const { + return iterator(m_owner, m_index + n); + } + + iterator operator-(difference_type n) const { + return iterator(m_owner, m_index - n); + } + + difference_type operator-(const iterator& other) const { + return static_cast(m_index) - + static_cast(other.m_index); + } + + iterator& operator+=(difference_type n) { + m_index += n; + return *this; + } + + iterator& operator-=(difference_type n) { + m_index -= n; + return *this; + } + + reference operator[](difference_type n) const { return *(*this + n); } + + bool operator<(const iterator& other) const { + return m_index < other.m_index; + } + + bool operator>(const iterator& other) const { + return m_index > other.m_index; + } + + bool operator<=(const iterator& other) const { + return m_index <= other.m_index; + } + + bool operator>=(const iterator& other) const { + return m_index >= other.m_index; + } + friend iterator operator+(difference_type n, const iterator& it) { + return it + n; + } + }; + + class const_iterator { // NOLINT + private: + const SparseVector* m_owner; + size_t m_index; + + public: + using iterator_category = std::random_access_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = const T*; + using reference = const T&; + + const_iterator(const SparseVector* owner, size_t index) + : m_owner(owner), m_index(index) {} + + reference operator*() const { + return m_owner->m_data[m_owner->m_dense[m_index]].value(); + } + + pointer operator->() const { return &(**this); } + + const_iterator& operator++() { + ++m_index; + return *this; + } + + const_iterator operator++(int) { + const_iterator tmp = *this; + ++(*this); + return tmp; + } + + const_iterator& operator--() { + --m_index; + return *this; + } + + const_iterator operator--(int) { + const_iterator tmp = *this; + --(*this); + return tmp; + } + + bool operator==(const const_iterator& other) const { + return m_owner == other.m_owner && m_index == other.m_index; + } + + bool operator!=(const const_iterator& other) const { + return !(*this == other); + } + + const_iterator operator+(difference_type n) const { + return const_iterator(m_owner, m_index + n); + } + + const_iterator operator-(difference_type n) const { + return const_iterator(m_owner, m_index - n); + } + + difference_type operator-(const const_iterator& other) const { + return static_cast(m_index) - + static_cast(other.m_index); + } + + const_iterator& operator+=(difference_type n) { + m_index += n; + return *this; + } + + const_iterator& operator-=(difference_type n) { + m_index -= n; + return *this; + } + + reference operator[](difference_type n) const { return *(*this + n); } + + bool operator<(const const_iterator& other) const { + return m_index < other.m_index; + } + + bool operator>(const const_iterator& other) const { + return m_index > other.m_index; + } + + bool operator<=(const const_iterator& other) const { + return m_index <= other.m_index; + } + + bool operator>=(const const_iterator& other) const { + return m_index >= other.m_index; + } + friend const_iterator operator+(difference_type n, + const const_iterator& it) { + return it + n; + } + }; + + template [[nodiscard]] Handle insert(U&& value) { + uint32_t id; + if (!m_free_list.empty()) { + id = m_free_list.back(); + m_free_list.pop_back(); + m_data[id].emplace(std::forward(value)); + + m_dense_index[id] = m_dense.size(); + } else { + id = m_data.size(); + m_data.push_back(std::forward(value)); + m_generation.push_back(1); + m_dense_index.emplace_back(m_dense.size()); + } + + m_dense.push_back(id); + return {id, m_generation[id]}; + } + + template Handle emplace(Args&&... args) { + return insert(T(std::forward(args)...)); + } + + void erase(Handle h) { + + if (!exists(h)) { + return; + } + uint32_t id = h.index; + m_free_list.push_back(id); + uint32_t index = m_dense_index[id]; + uint32_t last_id = m_dense.back(); + m_dense[index] = last_id; + m_dense_index[last_id] = index; + + m_dense.pop_back(); + m_data[id].reset(); + ++m_generation[id]; + } + + T& operator[](Handle h) { + assert(exists(h)); + return m_data[h.index].value(); + } + + const T& operator[](Handle h) const { + assert(exists(h)); + return m_data[h.index].value(); + } + + bool exists(Handle h) const { + return h.index < m_generation.size() && + m_generation[h.index] == h.generation; + } + + void reserve(size_t n) { + m_data.reserve(n); + m_generation.reserve(n); + m_dense.reserve(n); + m_dense_index.reserve(n); + } + + size_t size() const { return m_dense.size(); } + + bool empty() const { return m_dense.empty(); } + + iterator begin() { return iterator(this, 0); } + + iterator end() { return iterator(this, m_dense.size()); } + + const_iterator begin() const { return const_iterator(this, 0); } + + const_iterator end() const { return const_iterator(this, m_dense.size()); } + + const_iterator cbegin() const { return begin(); } + + const_iterator cend() const { return end(); } +}; + +} // namespace Cubed \ No newline at end of file diff --git a/include/Cubed/tools/threas_utils.hpp b/include/Cubed/tools/threas_utils.hpp new file mode 100644 index 0000000..a598433 --- /dev/null +++ b/include/Cubed/tools/threas_utils.hpp @@ -0,0 +1,130 @@ +#pragma once +#include "Cubed/gameplay/world.hpp" +#include "Cubed/tools/cubed_assert.hpp" + +#include +#include +namespace Cubed { +namespace Tools { + +constexpr size_t SERVER_RESERVED_THREADS = 3; // tick + netio + gen scheduler +constexpr size_t CLIENT_RESERVED_THREADS = + 3; // main/render + netio + system reserved + +constexpr size_t safe_sub(size_t a, size_t b) { return a > b ? a - b : 0; } + +inline size_t get_hardware_threads() { + auto hc = std::thread::hardware_concurrency(); + return hc == 0 ? 4 : static_cast(hc); +} + +inline size_t get_server_available_threads() { + return std::max( + 1, safe_sub(get_hardware_threads(), SERVER_RESERVED_THREADS)); +} + +inline size_t get_client_available_threads() { + return std::max( + 1, safe_sub(get_hardware_threads(), CLIENT_RESERVED_THREADS)); +} + +inline size_t get_client_threads(RunMode mode) { + switch (mode) { + case RunMode::SERVER_ONLY: + ASSERT_MSG(false, "Server Only don't need client pool"); + return 1; + case RunMode::CLIENT_ONLY: { + auto available = get_client_available_threads(); + return std::clamp(available, 1, 16); + } + + case RunMode::HYBRID: { + auto available = get_client_available_threads(); + return std::clamp(available / 2, 1, 4); + } + } + return 1; +} + +inline size_t get_server_net_pool_threads(RunMode mode) { + switch (mode) { + + case RunMode::SERVER_ONLY: { + auto available = get_server_available_threads(); + return std::clamp(available / 4, 1, + std::min(4, available)); + } + + case RunMode::CLIENT_ONLY: + ASSERT_MSG(false, "Client Only don't need net pool"); + return 1; + case RunMode::HYBRID: { + auto available = get_server_available_threads(); + return std::clamp(available / 8, 1, + std::min(4, available)); + } + } + return 1; +} + +inline size_t get_server_compute_treads(RunMode mode) { + switch (mode) { + case RunMode::HYBRID: + case RunMode::SERVER_ONLY: { + auto available = get_server_available_threads(); + + return std::clamp(available / 4, 1, + std::min(4, available)); + } + case RunMode::CLIENT_ONLY: + ASSERT_MSG(false, "Client Only don't need update pool"); + return 1; + } + return 1; +} + +inline size_t get_server_gen_threads(RunMode mode) { + switch (mode) { + case RunMode::SERVER_ONLY: { + auto available = get_server_available_threads(); + + auto net_pool = get_server_net_pool_threads(mode); + + auto update_pool = get_server_compute_treads(mode); + + size_t remain = available; + + remain -= std::min(remain, net_pool); + + remain -= std::min(remain, update_pool); + + return std::max(1, remain); + } + case RunMode::CLIENT_ONLY: + ASSERT_MSG(false, "Client Only don't need gen pool"); + return 1; + case RunMode::HYBRID: { + auto available = get_server_available_threads(); + + auto net_pool = get_server_net_pool_threads(mode); + + auto update_pool = get_server_compute_treads(mode); + + auto client = get_client_threads(mode) + CLIENT_RESERVED_THREADS; + + size_t remain = available; + + remain -= std::min(remain, net_pool); + + remain -= std::min(remain, update_pool); + + remain -= std::min(remain, client); + + return std::max(1, remain); + } + } + return 1; +} + +} // namespace Tools +} // namespace Cubed diff --git a/include/Cubed/tools/time_tools.hpp b/include/Cubed/tools/time_tools.hpp index 7d4add8..3c22df6 100644 --- a/include/Cubed/tools/time_tools.hpp +++ b/include/Cubed/tools/time_tools.hpp @@ -4,6 +4,7 @@ #include namespace Cubed { namespace Tools { +// return ms inline uint64_t get_time_ticks() { return SDL_GetTicks(); } } // namespace Tools diff --git a/include/Cubed/ui/inventory_ui.hpp b/include/Cubed/ui/inventory_ui.hpp index f53ea7e..c676e9e 100644 --- a/include/Cubed/ui/inventory_ui.hpp +++ b/include/Cubed/ui/inventory_ui.hpp @@ -20,7 +20,7 @@ private: Label* m_item_info = nullptr; Image* m_selected_image = nullptr; - BlockType m_selected_block = 0; + BlockType m_selected_id = 0; void update_item_info(); bool handle_mouse_button_event(const MouseButtonEvent& e) override; diff --git a/include/Cubed/ui/item_slot.hpp b/include/Cubed/ui/item_slot.hpp index ff06f8a..36bf015 100644 --- a/include/Cubed/ui/item_slot.hpp +++ b/include/Cubed/ui/item_slot.hpp @@ -1,6 +1,6 @@ #pragma once -#include "Cubed/gameplay/block.hpp" +#include "Cubed/gameplay/item.hpp" #include "Cubed/ui/image.hpp" #include "Cubed/ui/widget.hpp" namespace Cubed { @@ -13,11 +13,11 @@ public: ItemSlot& set_default_background(TextureManager& m_texture_manager); ItemSlot& set_scale(float m_scale); - ItemSlot& set_item(BlockType id, const Texture* texture); + ItemSlot& set_item(ItemID id, const Texture* texture); float width() const override; float height() const override; bool handle_mouse_move_event(const MouseMoveEvent& e) override; - BlockType block() const; + ItemID id() const; bool hovered() const; private: @@ -26,7 +26,7 @@ private: void on_update(float dt) override; std::unique_ptr m_background; std::unique_ptr m_foreground; - BlockType m_block_type; + ItemID m_id; float m_scale = 1.0f; bool m_hovered = false; }; diff --git a/include/nlohmann/.clang-format b/include/entt/.clang-format similarity index 100% rename from include/nlohmann/.clang-format rename to include/entt/.clang-format diff --git a/include/entt/config/config.h b/include/entt/config/config.h new file mode 100644 index 0000000..a200d29 --- /dev/null +++ b/include/entt/config/config.h @@ -0,0 +1,134 @@ +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +#if __has_include() +# include +#endif + +#include +#include "version.h" + +// NOLINTBEGIN(cppcoreguidelines-macro-usage) + +#ifdef ENTT_USE_STL +# define ENTT_FORCE_STL +#endif + +#if defined(__cpp_exceptions) && !defined(ENTT_NO_EXCEPTION) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#if defined(__cpp_consteval) +# define ENTT_CONSTEVAL consteval +#else +# define ENTT_CONSTEVAL constexpr +#endif + +#ifdef ENTT_USE_ATOMIC +# include "../stl/atomic.hpp" +# define ENTT_MAYBE_ATOMIC(Type) stl::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include "../stl/cstdint.hpp" +# define ENTT_ID_TYPE stl::uint32_t +#else +# include "../stl/cstdint.hpp" // 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 diff --git a/include/entt/config/macro.h b/include/entt/config/macro.h new file mode 100644 index 0000000..b7b2323 --- /dev/null +++ b/include/entt/config/macro.h @@ -0,0 +1,11 @@ +#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 diff --git a/include/entt/config/version.h b/include/entt/config/version.h new file mode 100644 index 0000000..d014997 --- /dev/null +++ b/include/entt/config/version.h @@ -0,0 +1,18 @@ +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +#include "macro.h" + +// NOLINTBEGIN(cppcoreguidelines-macro-*,modernize-macro-*) + +#define ENTT_VERSION_MAJOR 4 +#define ENTT_VERSION_MINOR 0 +#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 diff --git a/include/entt/container/dense_map.hpp b/include/entt/container/dense_map.hpp new file mode 100644 index 0000000..e4f5ee5 --- /dev/null +++ b/include/entt/container/dense_map.hpp @@ -0,0 +1,1026 @@ +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include "../config/config.h" +#include "../core/bit.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/iterator.hpp" +#include "../core/memory.hpp" +#include "../core/type_traits.hpp" +#include "../stl/bit.hpp" +#include "../stl/cmath.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/iterator.hpp" +#include "../stl/limits.hpp" +#include "../stl/memory.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +static constexpr stl::size_t dense_map_placeholder_position = (stl::numeric_limits::max)(); + +template +struct dense_map_node final { + using value_type = stl::pair; + + template + dense_map_node(const stl::size_t pos, Args &&...args) + : next{pos}, + element{stl::forward(args)...} {} + + template + dense_map_node(stl::allocator_arg_t, const auto &allocator, const stl::size_t pos, Args &&...args) + : next{pos}, + element{entt::make_obj_using_allocator(allocator, stl::forward(args)...)} {} + + dense_map_node(stl::allocator_arg_t, const auto &allocator, const dense_map_node &other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, other.element)} {} + + dense_map_node(stl::allocator_arg_t, const auto &allocator, dense_map_node &&other) + : next{other.next}, + element{entt::make_obj_using_allocator(allocator, stl::move(other.element))} {} + + stl::size_t next; + value_type element; +}; + +template +class dense_map_iterator final { + template + friend class dense_map_iterator; + + static_assert(stl::is_pointer_v, "Not a pointer type"); + using first_type = decltype(stl::as_const(stl::declval()->element.first)); + using second_type = decltype((stl::declval()->element.second)); + +public: + using value_type = stl::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template + requires (!stl::same_as && stl::constructible_from) + 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 + [[nodiscard]] constexpr stl::ptrdiff_t operator-(const dense_map_iterator &other) const noexcept { + return it - other.it; + } + + template + [[nodiscard]] constexpr bool operator==(const dense_map_iterator &other) const noexcept { + return it == other.it; + } + + template + [[nodiscard]] constexpr auto operator<=>(const dense_map_iterator &other) const noexcept { + return it <=> other.it; + } + +private: + It it; +}; + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + static_assert(stl::is_pointer_v, "Not a pointer type"); + using first_type = decltype(stl::as_const(stl::declval()->element.first)); + using second_type = decltype((stl::declval()->element.second)); + +public: + using value_type = stl::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept = default; + + constexpr dense_map_local_iterator(It iter, const stl::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template + requires (!stl::same_as && stl::constructible_from) + 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}; + } + + template + [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &other) const noexcept { + return offset == other.offset; + } + + [[nodiscard]] constexpr stl::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + stl::size_t offset{dense_map_placeholder_position}; +}; + +} // 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 stl::size_t minimum_capacity = 8u; + static constexpr stl::size_t placeholder_position = internal::dense_map_placeholder_position; + + using node_type = internal::dense_map_node; + using alloc_traits = stl::allocator_traits; + static_assert(stl::is_same_v>, "Invalid value type"); + using sparse_container_type = stl::vector>; + using packed_container_type = stl::vector>; + + [[nodiscard]] stl::size_t key_to_bucket(const auto &key) const noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + [[nodiscard]] auto constrained_find(const auto &key, const stl::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(); + } + + [[nodiscard]] auto constrained_find(const auto &key, const stl::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 stl::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], stl::piecewise_construct, stl::forward_as_tuple(stl::forward(key)), stl::forward_as_tuple(stl::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return stl::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 = stl::forward(value); + return stl::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], stl::forward(key), stl::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return stl::make_pair(--end(), true); + } + + void move_and_pop(const stl::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] = stl::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 = stl::pair; + /*! @brief Unsigned integer type. */ + using size_type = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::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{stl::piecewise_construct, stl::forward_as_tuple(other.sparse.first(), allocator), stl::forward_as_tuple(other.sparse.second())}, + packed{stl::piecewise_construct, stl::forward_as_tuple(other.packed.first(), allocator), stl::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{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.sparse.first()), allocator), stl::forward_as_tuple(stl::move(other.sparse.second()))}, + packed{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.packed.first()), allocator), stl::forward_as_tuple(stl::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 stl::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().data(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().data(); + } + + /** + * @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().data() + packed.first().size(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().data() + packed.first().size(); + } + + /** + * @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. + */ + stl::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + stl::pair insert(value_type &&value) { + return insert_or_do_nothing(stl::move(value.first), stl::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + requires stl::constructible_from + stl::pair insert(Arg &&value) { + return insert_or_do_nothing(stl::forward(value).first, stl::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @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. + */ + void insert(stl::input_iterator auto first, stl::input_iterator auto 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 + stl::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, stl::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + stl::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(stl::move(key), stl::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 + stl::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(stl::forward(args).first..., stl::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(stl::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), stl::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 stl::make_pair(it, false); + } + + stl::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return stl::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 + stl::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, stl::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + stl::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(stl::move(key), stl::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. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type const &at(const auto &key) const + requires is_transparent_v && is_transparent_v { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] mapped_type &at(const auto &key) + requires is_transparent_v && is_transparent_v { + 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(stl::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). + * @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 auto &key) const + requires is_transparent_v && is_transparent_v { + 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. + * @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 auto &key) + requires is_transparent_v && is_transparent_v { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const auto &key) const + requires is_transparent_v && is_transparent_v { + 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]] stl::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] stl::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. + * @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]] stl::pair equal_range(const auto &key) + requires is_transparent_v && is_transparent_v { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] stl::pair equal_range(const auto &key) const + requires is_transparent_v && is_transparent_v { + 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. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const auto &key) const + requires is_transparent_v && is_transparent_v { + 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().data(), 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().data(), 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(stl::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 = stl::bit_ceil(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 = stl::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(stl::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 ENTT_INTERNAL */ +#include + +namespace std { + +template +struct uses_allocator, Allocator> + : entt::stl::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif diff --git a/include/entt/container/dense_set.hpp b/include/entt/container/dense_set.hpp new file mode 100644 index 0000000..df71b72 --- /dev/null +++ b/include/entt/container/dense_set.hpp @@ -0,0 +1,890 @@ +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include "../config/config.h" +#include "../core/bit.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/type_traits.hpp" +#include "../stl/bit.hpp" +#include "../stl/cmath.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/iterator.hpp" +#include "../stl/limits.hpp" +#include "../stl/memory.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +static constexpr stl::size_t dense_set_placeholder_position = (stl::numeric_limits::max)(); + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + + static_assert(stl::is_pointer_v, "Not a pointer type"); + +public: + using value_type = stl::remove_const_t>::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template + requires (!stl::same_as && stl::constructible_from) + 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 stl::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + [[nodiscard]] constexpr stl::ptrdiff_t operator-(const dense_set_iterator &other) const noexcept { + return it - other.it; + } + + template + [[nodiscard]] constexpr bool operator==(const dense_set_iterator &other) const noexcept { + return it == other.it; + } + + template + [[nodiscard]] constexpr auto operator<=>(const dense_set_iterator &other) const noexcept { + return it <=> other.it; + } + +private: + It it; +}; + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + + static_assert(stl::is_pointer_v, "Not a pointer type"); + +public: + using value_type = stl::remove_const_t>::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept = default; + + constexpr dense_set_local_iterator(It iter, const stl::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template + requires (!stl::same_as && stl::constructible_from) + 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 stl::addressof(it[static_cast(offset)].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + template + [[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &other) const noexcept { + return offset == other.offset; + } + + [[nodiscard]] constexpr stl::size_t index() const noexcept { + return offset; + } + +private: + It it{}; + stl::size_t offset{dense_set_placeholder_position}; +}; + +} // 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 stl::size_t minimum_capacity = 8u; + static constexpr stl::size_t placeholder_position = internal::dense_set_placeholder_position; + + using node_type = stl::pair; + using alloc_traits = stl::allocator_traits; + static_assert(stl::is_same_v, "Invalid value type"); + using sparse_container_type = stl::vector>; + using packed_container_type = stl::vector>; + + [[nodiscard]] stl::size_t value_to_bucket(const auto &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + [[nodiscard]] auto constrained_find(const auto &value, const stl::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(); + } + + [[nodiscard]] auto constrained_find(const auto &value, const stl::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 stl::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], stl::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return stl::make_pair(--end(), true); + } + + void move_and_pop(const stl::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] = stl::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 = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::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 = stl::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = stl::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{stl::piecewise_construct, stl::forward_as_tuple(other.sparse.first(), allocator), stl::forward_as_tuple(other.sparse.second())}, + packed{stl::piecewise_construct, stl::forward_as_tuple(other.packed.first(), allocator), stl::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{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.sparse.first()), allocator), stl::forward_as_tuple(stl::move(other.sparse.second()))}, + packed{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.packed.first()), allocator), stl::forward_as_tuple(stl::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 stl::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().data(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().data(); + } + + /** + * @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().data() + packed.first().size(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().data() + packed.first().size(); + } + + /** + * @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 stl::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return stl::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 stl::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return stl::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. + */ + stl::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + stl::pair insert(value_type &&value) { + return insert_or_do_nothing(stl::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @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. + */ + void insert(stl::input_iterator auto first, stl::input_iterator auto 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 + stl::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && stl::is_same_v, value_type>)) { + return insert_or_do_nothing(stl::forward(args)...); + } else { + auto &node = packed.first().emplace_back(stl::piecewise_construct, stl::make_tuple(packed.first().size()), stl::forward_as_tuple(stl::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 stl::make_pair(it, false); + } + + stl::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return stl::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). + * @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 auto &key) const + requires is_transparent_v && is_transparent_v { + 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. + * @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 auto &value) + requires is_transparent_v && is_transparent_v { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const auto &value) const + requires is_transparent_v && is_transparent_v { + 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]] stl::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] stl::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. + * @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]] stl::pair equal_range(const auto &value) + requires is_transparent_v && is_transparent_v { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] stl::pair equal_range(const auto &value) const + requires is_transparent_v && is_transparent_v { + 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. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const auto &value) const + requires is_transparent_v && is_transparent_v { + 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().data(), 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().data(), 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(stl::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 = stl::bit_ceil(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 = stl::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(stl::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 diff --git a/include/entt/container/fwd.hpp b/include/entt/container/fwd.hpp new file mode 100644 index 0000000..72f0906 --- /dev/null +++ b/include/entt/container/fwd.hpp @@ -0,0 +1,38 @@ +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include "../stl/functional.hpp" +#include "../stl/memory.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" + +namespace entt { + +template< + typename Key, + typename Type, + typename = stl::hash, + typename = stl::equal_to<>, + typename = stl::allocator>> +class dense_map; + +template< + typename Type, + typename = stl::hash, + typename = stl::equal_to<>, + typename = stl::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 diff --git a/include/entt/container/table.hpp b/include/entt/container/table.hpp new file mode 100644 index 0000000..86e278d --- /dev/null +++ b/include/entt/container/table.hpp @@ -0,0 +1,434 @@ +#ifndef ENTT_CONTAINER_TABLE_HPP +#define ENTT_CONTAINER_TABLE_HPP + +#include "../config/config.h" +#include "../core/iterator.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/tuple.hpp" +#include "../stl/utility.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +class table_iterator { + template + friend class table_iterator; + +public: + using value_type = decltype(stl::forward_as_tuple(*stl::declval()...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::random_access_iterator_tag; + + constexpr table_iterator() noexcept + : it{} {} + + constexpr table_iterator(It... from) noexcept + : it{from...} {} + + template + requires (stl::constructible_from && ...) + constexpr table_iterator(const table_iterator &other) noexcept + : table_iterator{stl::get(other.it)...} {} + + constexpr table_iterator &operator++() noexcept { + return (++stl::get(it), ...), *this; + } + + constexpr table_iterator operator++(int) noexcept { + const table_iterator orig = *this; + return ++(*this), orig; + } + + constexpr table_iterator &operator--() noexcept { + return (--stl::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 ((stl::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 stl::forward_as_tuple(stl::get(it)[value]...); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return {operator[](0)}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + [[nodiscard]] constexpr stl::ptrdiff_t operator-(const table_iterator &other) const noexcept { + return stl::get<0>(it) - stl::get<0>(other.it); + } + + template + [[nodiscard]] constexpr bool operator==(const table_iterator &other) const noexcept { + return stl::get<0>(it) == stl::get<0>(other.it); + } + + template + [[nodiscard]] constexpr auto operator<=>(const table_iterator &other) const noexcept { + return stl::get<0>(it) <=> stl::get<0>(other.it); + } + +private: + stl::tuple it; +}; + +} // 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 = stl::tuple; + +public: + /*! @brief Unsigned integer type. */ + using size_type = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::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((((stl::get(payload).size() * sizeof...(Container)) == (stl::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{stl::move(container)...} { + ENTT_ASSERT((((stl::get(payload).size() * sizeof...(Container)) == (stl::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{stl::move(other.payload)} {} + + /** + * @brief Constructs the underlying containers using a given allocator. + * @param allocator A valid allocator. + */ + explicit basic_table(const auto &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((((stl::get(payload).size() * sizeof...(Container)) == (stl::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{stl::move(container), allocator}...} { + ENTT_ASSERT((((stl::get(payload).size() * sizeof...(Container)) == (stl::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{stl::move(stl::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 stl::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) { + (stl::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 stl::get<0>(payload).capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + (stl::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 stl::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 stl::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 {stl::get(payload).cbegin()...}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return {stl::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 {stl::get(payload).cend()...}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return {stl::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 {stl::get(payload).crbegin()...}; + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return {stl::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 {stl::get(payload).crend()...}; + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return {stl::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 + stl::tuple emplace(Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return stl::forward_as_tuple(stl::get(payload).emplace_back()...); + } else { + return stl::forward_as_tuple(stl::get(payload).emplace_back(stl::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 {stl::get(payload).erase(stl::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]] stl::tuple operator[](const size_type pos) const { + ENTT_ASSERT(pos < size(), "Index out of bounds"); + return stl::forward_as_tuple(stl::get(payload)[pos]...); + } + + /*! @copydoc operator[] */ + [[nodiscard]] stl::tuple operator[](const size_type pos) { + ENTT_ASSERT(pos < size(), "Index out of bounds"); + return stl::forward_as_tuple(stl::get(payload)[pos]...); + } + + /*! @brief Clears a table. */ + void clear() { + (stl::get(payload).clear(), ...); + } + +private: + container_type payload; +}; + +} // namespace entt + +/*! @cond ENTT_INTERNAL */ +#include + +namespace std { + +template +struct uses_allocator, Allocator> + : entt::stl::bool_constant<(entt::stl::uses_allocator_v && ...)> {}; + +} // namespace std +/*! @endcond */ + +#endif diff --git a/include/entt/core/algorithm.hpp b/include/entt/core/algorithm.hpp new file mode 100644 index 0000000..758f0cd --- /dev/null +++ b/include/entt/core/algorithm.hpp @@ -0,0 +1,143 @@ +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + +#include "../stl/algorithm.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/iterator.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" + +namespace entt { + +/** + * @brief Function object to wrap `stl::sort` in a class type. + * + * Unfortunately, `stl::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 `stl::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 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()(stl::random_access_iterator auto first, stl::random_access_iterator auto last, Compare compare = Compare{}, Args &&...args) const { + stl::sort(stl::forward(args)..., stl::move(first), stl::move(last), stl::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 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()(stl::random_access_iterator auto first, stl::random_access_iterator auto last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first + 1; it < last; ++it) { + auto value = stl::move(*it); + auto pre = it; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) + for(; pre > first && compare(value, *(pre - 1)); --pre) { + *pre = stl::move(*(pre - 1)); + } + // NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic) + + *pre = stl::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 +requires ((N % Bit) == 0) // The maximum number of bits to sort must be a multiple of the number of bits processed per pass +struct radix_sort { + /** + * @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 = stl::iterator_traits::value_type; + using difference_type = stl::iterator_traits::difference_type; + stl::vector aux(static_cast(stl::distance(first, last))); + + auto part = [getter = stl::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) + stl::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) + stl::size_t index[buckets]{}; + + for(stl::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)] = stl::move(*it); + } + }; + + for(stl::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); + stl::move(aux.begin(), aux.end(), first); + } + } + } +}; + +} // namespace entt + +#endif diff --git a/include/entt/core/any.hpp b/include/entt/core/any.hpp new file mode 100644 index 0000000..74e27d6 --- /dev/null +++ b/include/entt/core/any.hpp @@ -0,0 +1,623 @@ +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include "../config/config.h" +#include "../core/concepts.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/cstdint.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "fwd.hpp" +#include "type_info.hpp" +#include "type_traits.hpp" +#include "utility.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +enum class any_request : stl::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) stl::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: stl::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && stl::is_nothrow_move_constructible_v> {}; + +template +struct in_situ: stl::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) { + switch(const auto *elem = static_cast(value.data()); req) { + using enum internal::any_request; + case info: + return &type_id(); + case transfer: + if constexpr(stl::is_move_assignable_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + *const_cast(elem) = stl::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case assign: + if constexpr(stl::is_copy_assignable_v) { + *const_cast(elem) = *static_cast(other); + return other; + } + break; + case compare: + if constexpr(!stl::is_function_v && !stl::is_array_v && is_equality_comparable_v) { + return (*elem == *static_cast(other)) ? other : nullptr; + } else { + return (elem == other) ? other : nullptr; + } + case copy: + if constexpr(stl::is_copy_constructible_v) { + // NOLINTNEXTLINE(bugprone-casting-through-void) + static_cast(const_cast(other))->initialize(*elem); + } + break; + case 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{stl::move(*const_cast(elem))}; + } + } + + return nullptr; + } + + template + static void basic_deleter(const basic_any &value) { + ENTT_ASSERT((value.mode == any_policy::dynamic) || ((value.mode == any_policy::embedded) && !stl::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(stl::is_array_v) { + delete[] elem; + } else { + delete elem; + } + } + + template + void initialize([[maybe_unused]] Args &&...args) { + using plain_type = stl::remove_cvref_t; + + vtable = basic_vtable; + underlying_type = type_hash::value(); + + if constexpr(stl::is_void_v) { + deleter = nullptr; + mode = any_policy::empty; + this->instance = nullptr; + } else if constexpr(stl::is_lvalue_reference_v) { + deleter = nullptr; + mode = stl::is_const_v> ? any_policy::cref : any_policy::ref; + static_assert((stl::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + // NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion) + this->instance = (stl::addressof(args), ...); + } else if constexpr(in_situ_v) { + if constexpr(stl::is_trivially_destructible_v) { + deleter = nullptr; + } else { + deleter = &basic_deleter; + } + + mode = any_policy::embedded; + + if constexpr(stl::is_aggregate_v && (sizeof...(Args) != 0u || !stl::is_default_constructible_v)) { + ::new(&this->buffer) plain_type{stl::forward(args)...}; + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay) + ::new(&this->buffer) plain_type(stl::forward(args)...); + } + } else { + deleter = &basic_deleter; + mode = any_policy::dynamic; + + if constexpr(stl::is_aggregate_v && (sizeof...(Args) != 0u || !stl::is_default_constructible_v)) { + this->instance = new plain_type{stl::forward(args)...}; + } else if constexpr(stl::is_array_v) { + static_assert(sizeof...(Args) == 0u, "Invalid arguments"); + this->instance = new plain_type[stl::extent_v](); + } else { + this->instance = new plain_type(stl::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{stl::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(stl::in_place_type_t, Args &&...args) + : base_type{} { + initialize(stl::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 + requires (!stl::is_const_v && !stl::is_void_v) + explicit basic_any(stl::in_place_t, Type *value) + : base_type{} { + 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 + requires (!stl::same_as, basic_any>) + basic_any(Type &&value) + : basic_any{stl::in_place_type>, stl::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 = stl::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 = stl::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 + requires (!stl::same_as, basic_any>) + basic_any &operator=(Type &&value) { + emplace>(stl::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 { + 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)); + } + + /** + * @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(stl::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(stl::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(stl::is_const_v) { + return stl::as_const(*this).template data>(); + } else { + return (mode == any_policy::cref) ? nullptr : const_cast(stl::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(stl::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, stl::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 Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + basic_any other = stl::as_const(*this).as_ref(); + + switch(mode) { + using enum any_policy; + case cref: + case empty: + other.mode = mode; + break; + default: + other.mode = any_policy::ref; + break; + } + + 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]] stl::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]] stl::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]] stl::remove_const_t any_cast(basic_any &&data) noexcept { + if constexpr(stl::is_copy_constructible_v>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(stl::move(*instance)); + } + + return any_cast(data); + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(stl::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(stl::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&stl::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, stl::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] basic_any make_any(Args &&...args) { + return basic_any{stl::in_place_type, stl::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, stl::size_t Align = basic_any::alignment, typename Type> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{stl::in_place_type, stl::forward(value)}; +} + +} // namespace entt + +#endif diff --git a/include/entt/core/bit.hpp b/include/entt/core/bit.hpp new file mode 100644 index 0000000..d0698f2 --- /dev/null +++ b/include/entt/core/bit.hpp @@ -0,0 +1,26 @@ +#ifndef ENTT_CORE_BIT_HPP +#define ENTT_CORE_BIT_HPP + +#include "../config/config.h" +#include "../stl/bit.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" + +namespace entt { + +/** + * @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 Type fast_mod(const Type value, const stl::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(stl::has_single_bit(mod), "Value must be a power of two"); + return static_cast(value & (mod - 1u)); +} + +} // namespace entt + +#endif diff --git a/include/entt/core/compressed_pair.hpp b/include/entt/core/compressed_pair.hpp new file mode 100644 index 0000000..9f45225 --- /dev/null +++ b/include/entt/core/compressed_pair.hpp @@ -0,0 +1,266 @@ +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "fwd.hpp" +#include "type_traits.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + // NOLINTNEXTLINE(modernize-use-equals-default) + constexpr compressed_pair_element() noexcept(stl::is_nothrow_default_constructible_v) + requires stl::default_initializable {} + + template + constexpr compressed_pair_element(Arg &&arg) noexcept(stl::is_nothrow_constructible_v) + requires (!stl::same_as, compressed_pair_element>) + : value{stl::forward(arg)} {} + + template + constexpr compressed_pair_element(stl::tuple args, stl::index_sequence) noexcept(stl::is_nothrow_constructible_v) + : value{stl::forward(stl::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value{}; +}; + +template +requires is_ebco_eligible_v +struct compressed_pair_element: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + constexpr compressed_pair_element() noexcept(stl::is_nothrow_default_constructible_v) + requires stl::default_initializable + : base_type{} {} + + template + constexpr compressed_pair_element(Arg &&arg) noexcept(stl::is_nothrow_constructible_v) + requires (!stl::same_as, compressed_pair_element>) + : base_type{stl::forward(arg)} {} + + template + constexpr compressed_pair_element(stl::tuple args, stl::index_sequence) noexcept(stl::is_nothrow_constructible_v) + : base_type{stl::forward(stl::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. + */ + constexpr compressed_pair() noexcept(stl::is_nothrow_default_constructible_v && stl::is_nothrow_default_constructible_v) + requires stl::default_initializable && stl::default_initializable + : 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(stl::is_nothrow_constructible_v && stl::is_nothrow_constructible_v) + : first_base{stl::forward(arg)}, + second_base{stl::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(stl::piecewise_construct_t, stl::tuple args, stl::tuple other) noexcept(stl::is_nothrow_constructible_v && stl::is_nothrow_constructible_v) + : first_base{stl::move(args), stl::index_sequence_for{}}, + second_base{stl::move(other), stl::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 stl::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 + requires (Index <= 1u) + [[nodiscard]] constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + return second(); + } + } + + /*! @copydoc get */ + template + requires (Index <= 1u) + [[nodiscard]] constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + 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, stl::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 + +/*! @cond ENTT_INTERNAL */ +#include + +namespace std { + +template +struct tuple_size>: integral_constant {}; + +template +requires (Index <= 1u) +struct tuple_element>: conditional {}; + +} // namespace std +/*! @endcond */ + +#endif diff --git a/include/entt/core/concepts.hpp b/include/entt/core/concepts.hpp new file mode 100644 index 0000000..a8bb989 --- /dev/null +++ b/include/entt/core/concepts.hpp @@ -0,0 +1,17 @@ +#ifndef ENTT_CORE_CONCEPTS_HPP +#define ENTT_CORE_CONCEPTS_HPP + +#include "../stl/type_traits.hpp" + +namespace entt { + +/** + * @brief Specifies that a type is not a cv-qualified reference. + * @tparam Type Type to check. + */ +template +concept cvref_unqualified = stl::is_same_v, Type>; + +} // namespace entt + +#endif diff --git a/include/entt/core/enum.hpp b/include/entt/core/enum.hpp new file mode 100644 index 0000000..ed1b093 --- /dev/null +++ b/include/entt/core/enum.hpp @@ -0,0 +1,102 @@ +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include "../stl/concepts.hpp" +#include "../stl/type_traits.hpp" + +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: stl::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template +requires requires { + requires stl::is_enum_v; + { Type::_entt_enum_as_bitmask } -> stl::same_as; +} +struct enum_as_bitmask: stl::true_type {}; + +/** + * @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; + +/** + * @brief Specifies that an enum class supports bitmask operations. + * @tparam Type Enum class type. + */ +template +// check again that it is an enum to deal with incorrect specializations +concept enum_bitmask = stl::is_enum_v && enum_as_bitmask_v; + +} // 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 Type operator|(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr Type operator&(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr 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 Type operator~(const Type value) noexcept { + return static_cast(~static_cast>(value)); +} + +/*! @copydoc operator~ */ +template +[[nodiscard]] constexpr bool operator!(const Type value) noexcept { + return !static_cast>(value); +} + +/*! @copydoc operator| */ +template +constexpr Type &operator|=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template +constexpr Type &operator&=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template +constexpr Type &operator^=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs ^ rhs)); +} + +#endif diff --git a/include/entt/core/family.hpp b/include/entt/core/family.hpp new file mode 100644 index 0000000..953c12d --- /dev/null +++ b/include/entt/core/family.hpp @@ -0,0 +1,35 @@ +#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 value_type + inline static const value_type value = identifier(); +}; + +} // namespace entt + +#endif diff --git a/include/entt/core/fwd.hpp b/include/entt/core/fwd.hpp new file mode 100644 index 0000000..c454e8e --- /dev/null +++ b/include/entt/core/fwd.hpp @@ -0,0 +1,51 @@ +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include "../config/config.h" +#include "../stl/cstddef.hpp" +#include "../stl/cstdint.hpp" + +namespace entt { + +/*! @brief Possible modes of an any object. */ +enum class any_policy : stl::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 diff --git a/include/entt/core/hashed_string.hpp b/include/entt/core/hashed_string.hpp new file mode 100644 index 0000000..f3398af --- /dev/null +++ b/include/entt/core/hashed_string.hpp @@ -0,0 +1,260 @@ +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include "../stl/cstddef.hpp" +#include "../stl/cstdint.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +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 = stl::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 base_type::value_type *str) noexcept + : repr{str} {} + + const base_type::value_type *repr; + }; + +public: + /*! @brief Character type. */ + using value_type = base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = 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 Compares two hashed strings. + * @param other A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const noexcept { + return value() == other.value(); + } + + /** + * @brief Lexicographically compares two hashed strings. + * @param other A valid hashed string. + * @return The relative order between the two hashed strings. + */ + [[nodiscard]] constexpr auto operator<=>(const basic_hashed_string &other) const noexcept { + return value() <=> other.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, stl::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; + +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, stl::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, stl::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif diff --git a/include/entt/core/ident.hpp b/include/entt/core/ident.hpp new file mode 100644 index 0000000..3413393 --- /dev/null +++ b/include/entt/core/ident.hpp @@ -0,0 +1,35 @@ +#ifndef ENTT_CORE_IDENT_HPP +#define ENTT_CORE_IDENT_HPP + +#include "../stl/cstddef.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#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 ENTT_CONSTEVAL id_type get(stl::index_sequence) noexcept { + return (0 + ... + (stl::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 + requires (stl::is_same_v, Type> || ...) + static constexpr value_type value = get>(stl::index_sequence_for{}); +}; + +} // namespace entt + +#endif diff --git a/include/entt/core/iterator.hpp b/include/entt/core/iterator.hpp new file mode 100644 index 0000000..f389e23 --- /dev/null +++ b/include/entt/core/iterator.hpp @@ -0,0 +1,181 @@ +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" + +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(stl::is_nothrow_move_constructible_v) + : value{stl::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return stl::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 +struct iota_iterator final { + /*! @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 = stl::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = stl::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; + } + + /** + * @brief Comparison operator. + * @param other A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const iota_iterator &other) const noexcept { + return current == other.current; + } + +private: + value_type current; +}; + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template Sentinel = It> +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = stl::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(stl::is_nothrow_default_constructible_v && stl::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(stl::is_nothrow_move_constructible_v && stl::is_nothrow_move_constructible_v) + : first{stl::move(from)}, + last{stl::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 diff --git a/include/entt/core/memory.hpp b/include/entt/core/memory.hpp new file mode 100644 index 0000000..4ec18aa --- /dev/null +++ b/include/entt/core/memory.hpp @@ -0,0 +1,225 @@ +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include "../config/config.h" +#include "../stl/cstddef.hpp" +#include "../stl/memory.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" + +namespace entt { + +/** + * @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(stl::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(stl::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = stl::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(stl::allocator_traits::propagate_on_container_swap::value) { + using stl::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 = stl::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(stl::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(stl::is_nothrow_destructible_v) { + using alloc_traits = stl::allocator_traits; + alloc_traits::destroy(*this, stl::to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `stl::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 +constexpr auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!stl::is_array_v, "Array types are not supported"); + + using alloc_traits = stl::allocator_traits::template rebind_traits; + using allocator_type = alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, stl::to_address(ptr), stl::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return stl::unique_ptr>{ptr, alloc}; +} + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!stl::uses_allocator_v && stl::is_constructible_v) { + return stl::forward_as_tuple(stl::forward(params)...); + } else { + static_assert(stl::uses_allocator_v, "Ill-formed request"); + + if constexpr(stl::is_constructible_v) { + return stl::tuple{stl::allocator_arg, allocator, stl::forward(params)...}; + } else { + static_assert(stl::is_constructible_v, "Ill-formed request"); + return stl::forward_as_tuple(stl::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = stl::pair; + + template + static constexpr auto args(const auto &allocator, stl::piecewise_construct_t, First &&first, Second &&second) noexcept { + return stl::make_tuple( + stl::piecewise_construct, + stl::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, stl::forward(curr)...); }, stl::forward(first)), + stl::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, stl::forward(curr)...); }, stl::forward(second))); + } + + static constexpr auto args(const auto &allocator) noexcept { + return uses_allocator_construction::args(allocator, stl::piecewise_construct, stl::tuple<>{}, stl::tuple<>{}); + } + + template + static constexpr auto args(const auto &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, stl::piecewise_construct, stl::forward_as_tuple(stl::forward(first)), stl::forward_as_tuple(stl::forward(second))); + } + + template + static constexpr auto args(const auto &allocator, const stl::pair &value) noexcept { + return uses_allocator_construction::args(allocator, stl::piecewise_construct, stl::forward_as_tuple(value.first), stl::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const auto &allocator, stl::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, stl::piecewise_construct, stl::forward_as_tuple(stl::move(value.first)), stl::forward_as_tuple(stl::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 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 auto &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, stl::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 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 auto &allocator, Args &&...args) { + return stl::make_from_tuple(internal::uses_allocator_construction::args(allocator, stl::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 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 auto &allocator, Args &&...args) { + return stl::apply([value](auto &&...curr) { return ::new(value) Type(stl::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, stl::forward(args)...)); +} + +} // namespace entt + +#endif diff --git a/include/entt/core/monostate.hpp b/include/entt/core/monostate.hpp new file mode 100644 index 0000000..4116bda --- /dev/null +++ b/include/entt/core/monostate.hpp @@ -0,0 +1,60 @@ +#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 diff --git a/include/entt/core/ranges.hpp b/include/entt/core/ranges.hpp new file mode 100644 index 0000000..255d7d7 --- /dev/null +++ b/include/entt/core/ranges.hpp @@ -0,0 +1,22 @@ +#ifndef ENTT_CORE_RANGES_HPP +#define ENTT_CORE_RANGES_HPP + +#include + +#if defined(__cpp_lib_ranges) +# include +# include "iterator.hpp" + +namespace std::ranges { + +template +inline constexpr bool enable_borrowed_range>{true}; + +template +inline constexpr bool enable_view>{true}; + +} // namespace std::ranges + +#endif + +#endif diff --git a/include/entt/core/tuple.hpp b/include/entt/core/tuple.hpp new file mode 100644 index 0000000..fa55ad0 --- /dev/null +++ b/include/entt/core/tuple.hpp @@ -0,0 +1,90 @@ +#ifndef ENTT_CORE_TUPLE_HPP +#define ENTT_CORE_TUPLE_HPP + +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" + +namespace entt { + +/** + * @brief Provides the member constant `value` equal to true if a given type is + * a tuple, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_tuple: stl::false_type {}; + +/** + * @copybrief is_tuple + * @tparam Args Tuple template arguments. + */ +template +struct is_tuple>: stl::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(stl::tuple_size_v> == 1u) { + return stl::get<0>(stl::forward(value)); + } else { + return stl::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(stl::is_nothrow_constructible_v) + : Func{stl::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(stl::apply(stl::declval(), args))) { + return stl::apply(static_cast(*this), stl::forward(args)); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(stl::apply(stl::declval(), args))) { + return stl::apply(static_cast(*this), stl::forward(args)); + } +}; + +/** + * @brief Deduction guide. + * @tparam Func Type of underlying invocable object. + */ +template +forward_apply(Func) -> forward_apply>; + +} // namespace entt + +#endif diff --git a/include/entt/core/type_info.hpp b/include/entt/core/type_info.hpp new file mode 100644 index 0000000..1c251ab --- /dev/null +++ b/include/entt/core/type_info.hpp @@ -0,0 +1,232 @@ +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include "../config/config.h" +#include "../stl/string_view.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "fwd.hpp" +#include "hashed_string.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +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 stl::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 stl::string_view{}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] ENTT_CONSTEVAL stl::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] stl::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] ENTT_CONSTEVAL 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 stl::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator stl::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(stl::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 stl::string_view name() const noexcept { + return alias; + } + + /** + * @brief Compares two type info objects. + * @param other A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ + [[nodiscard]] constexpr bool operator==(const type_info &other) const noexcept { + return identifier == other.identifier; + } + + /** + * @brief Lexicographically compares two type info objects. + * @param other A type info object. + * @return The relative order between the two type info objects. + */ + [[nodiscard]] constexpr auto operator<=>(const type_info &other) const noexcept { + return seq <=> other.seq; + } + +private: + id_type seq; + id_type identifier; + stl::string_view alias; +}; + +/** + * @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(stl::is_same_v>) { + static const type_info instance{stl::in_place_type}; + return instance; + } else { + return type_id>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(const Type &) noexcept { + return type_id>(); +} + +} // namespace entt + +#endif diff --git a/include/entt/core/type_traits.hpp b/include/entt/core/type_traits.hpp new file mode 100644 index 0000000..2c63a61 --- /dev/null +++ b/include/entt/core/type_traits.hpp @@ -0,0 +1,909 @@ +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include "../config/config.h" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#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 ENTT_INTERNAL */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: stl::integral_constant {}; + +/*! @copydoc size_of */ +template +requires requires { sizeof(Type); } +struct size_of + // NOLINTNEXTLINE(bugprone-sizeof-expression) + : stl::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr stl::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 = stl::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 = 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 = stl::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 +requires (type_list_index>::value == sizeof...(Other)) +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = stl::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 = stl::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 stl::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 +ENTT_CONSTEVAL 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 = 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 = type_list_cat::type; + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : stl::conditional_t<(stl::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 = internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + */ +template +using type_list_unique_t = type_list_unique::type; + +/** + * @brief Provides the member constant `value` equal 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> + : stl::bool_constant<(stl::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 = 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 generates 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 = 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 = 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 = stl::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 +requires (value_list_index>::value == sizeof...(Other)) +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = stl::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 = stl::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 stl::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 +ENTT_CONSTEVAL 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 = 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 = 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 = stl::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 = value_list_unique::type; + +/** + * @brief Provides the member constant `value` equal 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> + : stl::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 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 = value_list_diff::type; + +/*! @brief Same as stl::is_invocable, but with tuples. */ +template +struct is_applicable: stl::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>: stl::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>: stl::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 stl::is_invocable_r, but with tuples for arguments. */ +template +struct is_applicable_r: stl::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>: stl::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` equal to true if a given type is + * complete, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_complete: stl::false_type {}; + +/*! @copydoc is_complete */ +template +requires requires { sizeof(Type); } +struct is_complete: stl::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` equal to true if a given type is + * an iterator, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_iterator: stl::false_type {}; + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct has_iterator_category: stl::false_type {}; + +template +requires requires { typename stl::iterator_traits::iterator_category; } +struct has_iterator_category: stl::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +requires (!stl::is_void_v>>) +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` equal 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: stl::bool_constant && !stl::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` equal to true if + * `Type::is_transparent` is valid and denotes a type, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_transparent: stl::false_type {}; + +/*! @copydoc is_transparent */ +template +requires requires { typename Type::is_transparent; } +struct is_transparent: stl::true_type {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_transparent_v = is_transparent::value; + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct has_tuple_size_value: stl::false_type {}; + +template +requires is_complete_v> +struct has_tuple_size_value: stl::true_type {}; + +template +struct has_value_type: stl::false_type {}; + +template +requires requires { typename Type::value_type; } +struct has_value_type: stl::true_type {}; + +template +[[nodiscard]] ENTT_CONSTEVAL bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] ENTT_CONSTEVAL bool unpack_maybe_equality_comparable(stl::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] ENTT_CONSTEVAL bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] ENTT_CONSTEVAL auto maybe_equality_comparable(int) -> decltype(stl::declval() == stl::declval()) { + return true; +} + +template +[[nodiscard]] ENTT_CONSTEVAL bool dispatch_is_equality_comparable() { + // NOLINTBEGIN(modernize-use-transparent-functors) + if constexpr(stl::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(stl::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else if constexpr(has_value_type::value) { + if constexpr(is_iterator_v || stl::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` equal to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: stl::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 = stl::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 = constness_as::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +class member_class; + +/** + * @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 +requires stl::is_member_pointer_v +class member_class { + 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 = stl::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = 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 ENTT_CONSTEVAL type_list pick_up(Ret (*)(Args...)); + + template + static ENTT_CONSTEVAL type_list pick_up(Ret (Class ::*)(Args...)); + + template + static ENTT_CONSTEVAL type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static ENTT_CONSTEVAL type_list pick_up(Type Class ::*); + + template + static ENTT_CONSTEVAL 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 = nth_argument::type; + +} // namespace entt + +template +struct entt::stl::tuple_size>: entt::stl::integral_constant::size> {}; + +template +struct entt::stl::tuple_element>: entt::type_list_element> {}; + +template +struct entt::stl::tuple_size>: entt::stl::integral_constant::size> {}; + +template +struct entt::stl::tuple_element>: entt::value_list_element> {}; + +#endif diff --git a/include/entt/core/utility.hpp b/include/entt/core/utility.hpp new file mode 100644 index 0000000..b7e50c4 --- /dev/null +++ b/include/entt/core/utility.hpp @@ -0,0 +1,84 @@ +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" + +namespace entt { + +/** + * @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(stl::is_nothrow_move_constructible_v) + : func{stl::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(stl::is_nothrow_invocable_v) { + return func(*this, stl::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(stl::is_nothrow_invocable_v) { + return func(*this, stl::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif diff --git a/include/entt/entity/component.hpp b/include/entt/entity/component.hpp new file mode 100644 index 0000000..6730fc3 --- /dev/null +++ b/include/entt/entity/component.hpp @@ -0,0 +1,59 @@ +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include "../config/config.h" +#include "../core/concepts.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/type_traits.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct in_place_delete: stl::bool_constant && stl::is_move_assignable_v)> {}; + +template<> +struct in_place_delete: stl::false_type {}; + +template +requires Type::in_place_delete +struct in_place_delete: stl::true_type {}; + +template +struct page_size: stl::integral_constant * ENTT_PACKED_PAGE> {}; + +template<> +struct page_size: stl::integral_constant {}; + +template +requires stl::is_convertible_v +struct page_size: stl::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 { + /*! @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 stl::size_t page_size = internal::page_size::value; +}; + +} // namespace entt + +#endif diff --git a/include/entt/entity/entity.hpp b/include/entt/entity/entity.hpp new file mode 100644 index 0000000..111574a --- /dev/null +++ b/include/entt/entity/entity.hpp @@ -0,0 +1,312 @@ +#ifndef ENTT_ENTITY_ENTITY_HPP +#define ENTT_ENTITY_ENTITY_HPP + +#include "../config/config.h" +#include "../core/bit.hpp" +#include "../stl/bit.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/cstdint.hpp" +#include "../stl/type_traits.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct entt_traits; + +template +requires requires { + requires stl::is_enum_v; + typename internal::entt_traits>::value_type; +} +struct entt_traits: entt_traits> { + using value_type = Type; +}; + +template +requires requires { typename Type::entity_type; } +struct entt_traits + : entt_traits { + using value_type = Type; +}; + +template<> +struct entt_traits { + using value_type = stl::uint32_t; + + using entity_type = stl::uint32_t; + using version_type = stl::uint16_t; + + static constexpr entity_type entity_mask = 0xFFFFF; + static constexpr entity_type version_mask = 0xFFF; +}; + +template<> +struct entt_traits { + using value_type = stl::uint64_t; + + using entity_type = stl::uint64_t; + using version_type = stl::uint32_t; + + static constexpr entity_type entity_mask = 0xFFFFFFFF; + static constexpr entity_type version_mask = 0xFFFFFFFF; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Specifies that a type is an entity-like type. + * @tparam Type Type to check. + */ +template +concept entity_like = requires { + typename internal::entt_traits::value_type; +}; + +/** + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. + */ +template +class basic_entt_traits { + static constexpr auto length = stl::popcount(Traits::entity_mask); + +public: + /*! @brief Value type. */ + using value_type = Traits::value_type; + /*! @brief Underlying entity type. */ + using entity_type = Traits::entity_type; + /*! @brief Underlying version type. */ + using version_type = 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 { + static_assert(Traits::entity_mask && ((Traits::entity_mask & (Traits::entity_mask + 1)) == 0), "Invalid entity mask"); + 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 { + static_assert((Traits::version_mask & (Traits::version_mask + 1)) == 0, "Invalid version mask"); + 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 stl::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 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 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 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; + return traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + } + + /** + * @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 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 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; + return traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + } + + /** + * @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 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 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 diff --git a/include/entt/entity/fwd.hpp b/include/entt/entity/fwd.hpp new file mode 100644 index 0000000..f1d1a89 --- /dev/null +++ b/include/entt/entity/fwd.hpp @@ -0,0 +1,291 @@ +#ifndef ENTT_ENTITY_FWD_HPP +#define ENTT_ENTITY_FWD_HPP + +#include "../config/config.h" +#include "../core/concepts.hpp" +#include "../core/fwd.hpp" +#include "../core/type_traits.hpp" +#include "../stl/cstdint.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" + +namespace entt { + +/*! @brief Default entity identifier. */ +enum class entity : id_type {}; + +/*! @brief Storage deletion policy. */ +enum class deletion_policy : stl::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> +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 ENTT_CONSTEVAL 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 ENTT_CONSTEVAL 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 ENTT_CONSTEVAL 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> +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 = 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 = 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 diff --git a/include/entt/entity/group.hpp b/include/entt/entity/group.hpp new file mode 100644 index 0000000..dcb1714 --- /dev/null +++ b/include/entt/entity/group.hpp @@ -0,0 +1,1052 @@ +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#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 "../stl/array.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +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(stl::is_void_v) { + return stl::make_tuple(); + } else { + return stl::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + using iterator_type = It; + using value_type = decltype(stl::tuple_cat(stl::make_tuple(*stl::declval()), stl::declval().get_as_tuple({})..., stl::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::forward_iterator_tag; + + constexpr extended_group_iterator() + : it{}, + pools{} {} + + extended_group_iterator(iterator_type from, stl::tuple cpools) + : it{from}, + pools{stl::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 stl::tuple_cat(stl::make_tuple(*it), index_to_element(*stl::get(pools))..., stl::get(pools)->get_as_tuple(*it)...); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + [[nodiscard]] constexpr bool operator==(const extended_group_iterator &other) const noexcept { + return it == other.it; + } + +private: + It it; + stl::tuple pools; +}; + +struct group_descriptor { + using size_type = stl::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 = Type::entity_type; + + void swap_elements(const stl::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(stl::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools) + && stl::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(stl::apply([entt, pos = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < pos) && (other->contains(entt) && ...); }, pools) + && stl::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 = Type::size_type; + + template + group_handler(stl::tuple ogpool, stl::tuple epool) + : pools{stl::apply([](auto &&...cpool) { return stl::array{&cpool...}; }, ogpool)}, + filter{stl::apply([](auto &&...cpool) { return stl::array{&cpool...}; }, epool)} { + stl::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); + stl::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: + stl::array pools; + stl::array filter; + stl::size_t len{}; +}; + +template +class group_handler final: public group_descriptor { + using entity_type = Type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && stl::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && stl::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && stl::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && stl::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, stl::tuple gpool, stl::tuple epool) + : pools{stl::apply([](auto &&...cpool) { return stl::array{&cpool...}; }, gpool)}, + filter{stl::apply([](auto &&...cpool) { return stl::array{&cpool...}; }, epool)}, + elem{allocator} { + stl::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); + stl::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: + stl::array pools; + stl::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 = stl::common_type_t; + using underlying_type = base_type::entity_type; + + template + static constexpr stl::size_t index_of = type_list_index_v, type_list>; + + template + [[nodiscard]] auto pools_for(stl::index_sequence) const noexcept { + using return_type = stl::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 = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::ptrdiff_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = common_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = 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(stl::index_sequence_for{}); + + if constexpr(sizeof...(Index) == 0) { + return stl::apply([entt](auto *...curr) { return stl::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (stl::get(cpools)->get(entt), ...); + } else { + return stl::tuple_cat(stl::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{}, stl::declval().get({})))>) { + stl::apply(func, stl::tuple_cat(stl::make_tuple(entt), get(entt))); + } else { + stl::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(stl::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(stl::tuple, stl::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...>(stl::move(compare), stl::move(algo), stl::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(stl::is_invocable_v, "Invalid comparison function"); + descriptor->handle().sort(stl::move(compare), stl::move(algo), stl::forward(args)...); + } else { + auto comp = [&compare, cpools = pools_for(stl::index_sequence_for{})](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((stl::get(cpools)->get(lhs), ...), (stl::get(cpools)->get(rhs), ...)); + } else { + return compare(stl::forward_as_tuple(stl::get(cpools)->get(lhs)...), stl::forward_as_tuple(stl::get(cpools)->get(rhs)...)); + } + }; + + descriptor->handle().sort(stl::move(comp), stl::move(algo), stl::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. + * + * @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 sort_as(stl::input_iterator auto first, stl::input_iterator auto 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 = stl::common_type_t; + using underlying_type = base_type::entity_type; + + template + static constexpr stl::size_t index_of = type_list_index_v, type_list>; + + template + [[nodiscard]] auto pools_for(stl::index_sequence, stl::index_sequence) const noexcept { + using return_type = stl::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 = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::ptrdiff_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = common_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = 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(stl::index_sequence_for{}, stl::index_sequence_for{}); + + if constexpr(sizeof...(Index) == 0) { + return stl::apply([entt](auto *...curr) { return stl::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (stl::get(cpools)->get(entt), ...); + } else { + return stl::tuple_cat(stl::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{}, stl::declval().get({})))>) { + stl::apply(func, args); + } else { + stl::apply([&func](auto, auto &&...less) { func(stl::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(stl::index_sequence_for{}, stl::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(stl::tuple, stl::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...>(stl::move(compare), stl::move(algo), stl::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(stl::index_sequence_for{}, stl::index_sequence_for{}); + + if constexpr(sizeof...(Index) == 0) { + static_assert(stl::is_invocable_v, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), stl::move(compare), stl::move(algo), stl::forward(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((stl::get(cpools)->get(lhs), ...), (stl::get(cpools)->get(rhs), ...)); + } else { + return compare(stl::forward_as_tuple(stl::get(cpools)->get(lhs)...), stl::forward_as_tuple(stl::get(cpools)->get(rhs)...)); + } + }; + + storage<0>()->sort_n(descriptor->length(), stl::move(comp), stl::move(algo), stl::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), ...); + } + }; + + stl::apply(cb, cpools); + } + +private: + handler *descriptor; +}; + +} // namespace entt + +#endif diff --git a/include/entt/entity/handle.hpp b/include/entt/entity/handle.hpp new file mode 100644 index 0000000..a14016f --- /dev/null +++ b/include/entt/entity/handle.hpp @@ -0,0 +1,368 @@ +#ifndef ENTT_ENTITY_HANDLE_HPP +#define ENTT_ENTITY_HANDLE_HPP + +#include "../config/config.h" +#include "../core/iterator.hpp" +#include "../core/type_traits.hpp" +#include "../stl/iterator.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +class handle_storage_iterator final { + template + friend class handle_storage_iterator; + + using underlying_type = stl::remove_reference_t; + using entity_type = underlying_type::entity_type; + +public: + using value_type = stl::iterator_traits::value_type; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::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 + [[nodiscard]] constexpr bool operator==(const handle_storage_iterator &other) const noexcept { + return it == other.it; + } + +private: + entity_type entt; + It it; + It last; +}; + +} // 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 = traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = stl::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(stl::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(stl::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) || ... || stl::is_same_v), "Invalid type"); + return owner_or_assert().template emplace(entt, stl::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) || ... || stl::is_same_v), "Invalid type"); + return owner_or_assert().template emplace_or_replace(entt, stl::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) || ... || stl::is_same_v), "Invalid type"); + return owner_or_assert().template patch(entt, stl::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) || ... || stl::is_same_v), "Invalid type"); + return owner_or_assert().template replace(entt, stl::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) || ... || stl::is_same_v), "Invalid type"); + return owner_or_assert().template get_or_emplace(entt, stl::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 Compares two handles. + * @tparam Other Scope of the other handle. + * @param other 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 &other) const noexcept { + return owner == other.registry() && entt == other.entity(); + } + + /** + * @brief Compares a handle with the null object. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ + [[nodiscard]] constexpr bool operator==(const null_t other) const noexcept { + return (entt == other); + } + + /** + * @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(stl::is_same_v || stl::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; +}; + +} // namespace entt + +#endif diff --git a/include/entt/entity/helper.hpp b/include/entt/entity/helper.hpp new file mode 100644 index 0000000..3a89c2f --- /dev/null +++ b/include/entt/entity/helper.hpp @@ -0,0 +1,256 @@ +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include "../core/fwd.hpp" +#include "../core/type_traits.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.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 = 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(stl::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 = 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(stl::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 +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(stl::size_t pos{}, count = storage.size(); pos < count; pos += traits_type::page_size, ++page) { + if(const auto dist = (stl::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(stl::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(stl::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(stl::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 diff --git a/include/entt/entity/mixin.hpp b/include/entt/entity/mixin.hpp new file mode 100644 index 0000000..d5a15b9 --- /dev/null +++ b/include/entt/entity/mixin.hpp @@ -0,0 +1,593 @@ +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP + +#include "../config/config.h" +#include "../core/any.hpp" +#include "../core/type_info.hpp" +#include "../signal/sigh.hpp" +#include "../stl/concepts.hpp" +#include "../stl/iterator.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct has_on_construct final: stl::false_type {}; + +template +requires stl::invocable +struct has_on_construct: stl::true_type {}; + +template +struct has_on_update final: stl::false_type {}; + +template +requires stl::invocable +struct has_on_update: stl::true_type {}; + +template +struct has_on_destroy final: stl::false_type {}; + +template +requires stl::invocable +struct has_on_destroy: stl::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 = underlying_type::base_type::basic_iterator; + + static_assert(stl::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(stl::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 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(!stl::is_same_v) { + if(owner == nullptr) { + owner = any_cast(&value); + } + } + + underlying_type::bind_any(stl::move(value)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = 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{stl::move(other.construction)}, + destruction{stl::move(other.destruction)}, + update{stl::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{stl::move(other.construction), allocator}, + destruction{stl::move(other.destruction), allocator}, + update{stl::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 stl::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 output 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 It> + 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, stl::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, stl::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 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(stl::input_iterator auto first, stl::input_iterator auto last, Args &&...args) { + auto from = underlying_type::size(); + underlying_type::insert(first, last, stl::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 = stl::allocator_traits; + using basic_registry_type = basic_registry; + using container_type = stl::vector>; + + static_assert(stl::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 &, 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(!stl::is_same_v) { + if(owner == nullptr) { + owner = any_cast(&value); + } + } + + underlying_type::bind_any(stl::move(value)); + } + +public: + /*! @brief Allocator type. */ + using allocator_type = underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = 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{stl::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{stl::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(stl::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(stl::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(stl::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{}) { + stl::conditional_t<((stl::is_const_v && ...) && (stl::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 diff --git a/include/entt/entity/organizer.hpp b/include/entt/entity/organizer.hpp new file mode 100644 index 0000000..711d4b4 --- /dev/null +++ b/include/entt/entity/organizer.hpp @@ -0,0 +1,437 @@ +#ifndef ENTT_ENTITY_ORGANIZER_HPP +#define ENTT_ENTITY_ORGANIZER_HPP + +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../core/utility.hpp" +#include "../graph/adjacency_matrix.hpp" +#include "../graph/flow.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "fwd.hpp" +#include "helper.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct is_view: stl::false_type {}; + +template +struct is_view>: stl::true_type {}; + +template +inline constexpr bool is_view_v = is_view::value; + +template +struct is_group: stl::false_type {}; + +template +struct is_group>: stl::true_type {}; + +template +inline constexpr bool is_group_v = is_group::value; + +template +struct unpack_type { + using ro = stl::conditional_t< + type_list_contains_v || (stl::is_const_v && !type_list_contains_v>), + type_list>, + type_list<>>; + + using rw = stl::conditional_t< + type_list_contains_v> || (!stl::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 = (stl::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 = stl::size_t(const bool, const type_info **, const stl::size_t); + + struct vertex_data final { + stl::size_t ro_count{}; + stl::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(stl::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 stl::tuple(reg))...>(extract(reg)...); + } + + template + [[nodiscard]] static stl::size_t fill_dependencies(type_list, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const stl::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(stl::size_t pos{}; pos < length; ++pos) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + buffer[pos] = info[pos]; + } + + return length; + } + } + + template + void track_dependencies(stl::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 = registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = stl::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, stl::vector from, stl::vector to) + : node{stl::move(data)}, + in{stl::move(from)}, + out{stl::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 stl::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 stl::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 stl::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 stl::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; + stl::vector in; + stl::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 ®) { + stl::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 stl::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(stl::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)); + stl::apply(Candidate, stl::tuple_cat(stl::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 stl::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(stl::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 stl::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(stl::move(vdata)); + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency list of the task graph. + */ + [[nodiscard]] stl::vector graph() const { + stl::vector adjacency_list{}; + adjacency_list.reserve(vertices.size()); + + for(auto adjacency_matrix = builder.graph(); auto curr: adjacency_matrix.vertices()) { + stl::vector in{}; + stl::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], stl::move(in), stl::move(out)); + } + + return adjacency_list; + } + + /*! @brief Erases all elements from a container. */ + void clear() { + builder.clear(); + vertices.clear(); + } + +private: + stl::vector vertices; + flow builder; +}; + +} // namespace entt + +#endif diff --git a/include/entt/entity/ranges.hpp b/include/entt/entity/ranges.hpp new file mode 100644 index 0000000..284e5f5 --- /dev/null +++ b/include/entt/entity/ranges.hpp @@ -0,0 +1,28 @@ +#ifndef ENTT_ENTITY_RANGES_HPP +#define ENTT_ENTITY_RANGES_HPP + +#include + +#if defined(__cpp_lib_ranges) +# include +# include "fwd.hpp" + +namespace std::ranges { + +template +inline constexpr bool enable_borrowed_range>{true}; + +template +inline constexpr bool enable_borrowed_range>{true}; + +template +inline constexpr bool enable_view>{true}; + +template +inline constexpr bool enable_view>{true}; + +} // namespace std::ranges + +#endif + +#endif diff --git a/include/entt/entity/registry.hpp b/include/entt/entity/registry.hpp new file mode 100644 index 0000000..4123b5c --- /dev/null +++ b/include/entt/entity/registry.hpp @@ -0,0 +1,1181 @@ +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + +#include +#include "../config/config.h" +#include "../container/dense_map.hpp" +#include "../core/algorithm.hpp" +#include "../core/any.hpp" +#include "../core/concepts.hpp" +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "../core/memory.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../stl/algorithm.hpp" +#include "../stl/array.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/iterator.hpp" +#include "../stl/memory.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "entity.hpp" +#include "fwd.hpp" +#include "group.hpp" +#include "mixin.hpp" +#include "sparse_set.hpp" +#include "storage.hpp" +#include "view.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +class registry_storage_iterator final { + template + friend class registry_storage_iterator; + + using mapped_type = stl::remove_reference_t()->second)>; + +public: + using value_type = stl::pair &>; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::random_access_iterator_tag; + + constexpr registry_storage_iterator() noexcept + : it{} {} + + constexpr registry_storage_iterator(It iter) noexcept + : it{iter} {} + + template + requires (!stl::same_as && stl::constructible_from) + 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 + [[nodiscard]] constexpr stl::ptrdiff_t operator-(const registry_storage_iterator &other) const noexcept { + return it - other.it; + } + + template + [[nodiscard]] constexpr bool operator==(const registry_storage_iterator &other) const noexcept { + return it == other.it; + } + + template + [[nodiscard]] constexpr auto operator<=>(const registry_storage_iterator &other) const noexcept { + return it <=> other.it; + } + +private: + It it; +}; + +template +class registry_context { + using alloc_traits = stl::allocator_traits; + using allocator_type = 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, stl::in_place_type, stl::forward(args)...).first->second); + } + + template + Type &emplace(Args &&...args) { + return emplace_as(type_id().hash(), stl::forward(args)...); + } + + template + Type &insert_or_assign(const id_type id, Type &&value) { + return any_cast &>(ctx.insert_or_assign(id, stl::forward(value)).first->second); + } + + template + Type &insert_or_assign(Type &&value) { + return insert_or_assign(type_id().hash(), stl::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, stl::identity, stl::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 = stl::allocator_traits; + static_assert(stl::is_same_v, "Invalid value type"); + // stl::shared_ptr because of its type erased allocator which is useful here + using pool_container_type = dense_map, stl::identity, stl::equal_to<>, typename alloc_traits::template rebind_alloc>>>; + using group_container_type = dense_map, stl::identity, stl::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()) { + if constexpr(stl::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); + } + + typename pool_container_type::mapped_type cpool = stl::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 { + if constexpr(stl::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 = traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = stl::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 = 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{stl::move(other.vars)}, + pools{stl::move(other.pools)}, + groups{stl::move(other.groups)}, + entities{stl::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 stl::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(stl::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 output 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 It> + void create(It first, It last) { + entities.generate(stl::move(first), stl::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 + * + * @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 destroy(stl::input_iterator auto first, stl::input_iterator auto 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, stl::forward(args)...); + } + + /** + * @brief Assigns each entity in a range the given element. + * + * @sa emplace + * + * @tparam Type Type of element to create. + * @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(stl::input_iterator auto first, stl::input_iterator auto last, const Type &value = {}) { + ENTT_ASSERT(stl::all_of(first, last, [this](const auto entt) { return valid(entt); }), "Invalid entity"); + assure().insert(stl::move(first), stl::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 + requires stl::same_as::value_type, Type> + void insert(EIt first, EIt last, CIt from) { + ENTT_ASSERT(stl::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{stl::forward(args)...}), ...); }) : cpool.emplace(entt, stl::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, stl::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{stl::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(stl::is_same_v) { + stl::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()) { + stl::swap((*from), cpools.back()); + } + } + + count += (*from)->remove(first, last); + } + + } else { + for(auto cpools = stl::forward_as_tuple(assure(), assure()...); first != last; ++first) { + count += stl::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(stl::is_same_v) { + stl::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()) { + stl::swap(*from, cpools.back()); + } + } + + (*from)->erase(first, last); + } + } else { + for(auto cpools = stl::forward_as_tuple(assure(), assure()...); first != last; ++first) { + stl::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, stl::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 stl::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 stl::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, stl::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)) ? stl::addressof(cpool->get(entt)) : nullptr; + } else { + return stl::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(stl::as_const(*this).template try_get(entt)), ...); + } else { + return stl::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 stl::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 = group_type::handler; + + if(auto it = groups.find(group_type::group_id()); it != groups.cend()) { + return {*stl::static_pointer_cast(it->second)}; + } + + stl::shared_ptr handler{}; + + if constexpr(sizeof...(Owned) == 0u) { + handler = stl::allocate_shared(get_allocator(), get_allocator(), stl::forward_as_tuple(assure>()...), stl::forward_as_tuple(assure>()...)); + } else { + handler = stl::allocate_shared(get_allocator(), stl::forward_as_tuple(assure>()..., assure>()...), stl::forward_as_tuple(assure>()...)); + ENTT_ASSERT(stl::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 = group_type::handler; + + if(auto it = groups.find(group_type::group_id()); it != groups.cend()) { + return {*stl::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 stl::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(stl::is_invocable_v) { + auto comp = [&cpool, compare = stl::move(compare)](const auto lhs, const auto rhs) { return compare(stl::as_const(cpool.get(lhs)), stl::as_const(cpool.get(rhs))); }; + cpool.sort(stl::move(comp), stl::move(algo), stl::forward(args)...); + } else { + cpool.sort(stl::move(compare), stl::move(algo), stl::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 diff --git a/include/entt/entity/runtime_view.hpp b/include/entt/entity/runtime_view.hpp new file mode 100644 index 0000000..1c69c8d --- /dev/null +++ b/include/entt/entity/runtime_view.hpp @@ -0,0 +1,322 @@ +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + +#include "../stl/algorithm.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +class runtime_view_iterator final { + using iterator_type = Set::iterator; + using iterator_traits = stl::iterator_traits; + + [[nodiscard]] bool valid() const { + return (!tombstone_check || *it != tombstone) + && stl::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) + && stl::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); + } + +public: + using value_type = iterator_traits::value_type; + using pointer = iterator_traits::pointer; + using reference = iterator_traits::reference; + using difference_type = iterator_traits::difference_type; + using iterator_category = stl::bidirectional_iterator_tag; + + constexpr runtime_view_iterator() noexcept + : pools{}, + filter{}, + it{}, + tombstone_check{} {} + + runtime_view_iterator(const stl::vector &cpools, iterator_type curr, const stl::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; + } + +private: + const stl::vector *pools; + const stl::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 = stl::allocator_traits; + static_assert(stl::is_same_v, "Invalid value type"); + using container_type = stl::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 = Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::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{stl::move(other.pools), allocator}, + filter{stl::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 stl::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(stl::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() + && stl::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) + && stl::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 diff --git a/include/entt/entity/snapshot.hpp b/include/entt/entity/snapshot.hpp new file mode 100644 index 0000000..fd14b26 --- /dev/null +++ b/include/entt/entity/snapshot.hpp @@ -0,0 +1,509 @@ +#ifndef ENTT_ENTITY_SNAPSHOT_HPP +#define ENTT_ENTITY_SNAPSHOT_HPP + +#include "../config/config.h" +#include "../container/dense_map.hpp" +#include "../core/type_traits.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "entity.hpp" +#include "fwd.hpp" +#include "view.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +void orphans(Registry ®istry) { + for(auto &storage = registry.template storage(); 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(!stl::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 = 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(stl::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); + stl::apply([&archive](auto &&...args) { (archive(stl::forward(args)), ...); }, storage->get_as_tuple(entt)); + } + } + } else { + for(auto elem: storage->reach()) { + stl::apply([&archive](auto &&...args) { (archive(stl::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. + * @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, stl::input_iterator auto first, stl::input_iterator auto last, const id_type id = type_hash::value()) const { + static_assert(!stl::is_same_v, "Entity types not supported"); + + if(const auto *storage = reg->template storage(id); storage && !storage->empty()) { + archive(static_cast(stl::distance(first, last))); + + for(; first != last; ++first) { + if(const auto entt = *first; storage->contains(entt)) { + archive(entt); + stl::apply([&archive](auto &&...args) { (archive(stl::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(!stl::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 = 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(stl::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(stl::tuple_size_v == 0u) { + storage.emplace(entity); + } else { + Type elem{}; + archive(elem); + storage.emplace(entity, stl::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(!stl::is_const_v, "Non-const registry type required"); + using traits_type = entt_traits; + + void restore(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, stl::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 = stl::remove_const_t::first_type>; + using second_type = stl::decay_t::second_type; + + if constexpr(stl::is_same_v && stl::is_same_v) { + other.emplace(map(pair.first), map(pair.second)); + } else if constexpr(stl::is_same_v) { + other.emplace(map(pair.first), stl::move(pair.second)); + } else { + static_assert(stl::is_same_v, "Neither the key nor the value are of entity type"); + other.emplace(stl::move(pair.first), map(pair.second)); + } + } + + using stl::swap; + swap(container, other); + } + + template + auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) { + // vector like container + static_assert(stl::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(!stl::is_same_v) { + return; + } else if constexpr(stl::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 = 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(stl::is_same_v) { + typename traits_type::entity_type in_use{}; + + storage.reserve(length); + archive(in_use); + + for(stl::size_t pos{}; pos < in_use; ++pos) { + archive(entt); + restore(entt); + } + + for(stl::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(stl::tuple_size_v == 0u) { + storage.emplace(map(entt)); + } else { + Type elem{}; + archive(elem); + storage.emplace(map(entt), stl::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 diff --git a/include/entt/entity/sparse_set.hpp b/include/entt/entity/sparse_set.hpp new file mode 100644 index 0000000..39540e3 --- /dev/null +++ b/include/entt/entity/sparse_set.hpp @@ -0,0 +1,1076 @@ +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + +#include +#include "../config/config.h" +#include "../core/algorithm.hpp" +#include "../core/any.hpp" +#include "../core/bit.hpp" +#include "../core/type_info.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct sparse_set_iterator final { + using value_type = Container::value_type; + using pointer = Container::const_pointer; + using reference = Container::const_reference; + using difference_type = Container::difference_type; + using iterator_category = stl::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 stl::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + [[nodiscard]] constexpr stl::ptrdiff_t operator-(const sparse_set_iterator &other) const noexcept { + // intentionally reversed due to backward iteration + return other.offset - offset; + } + + [[nodiscard]] constexpr bool operator==(const sparse_set_iterator &other) const noexcept { + return offset == other.offset; + } + + [[nodiscard]] constexpr auto operator<=>(const sparse_set_iterator &other) const noexcept { + // intentionally reversed due to backward iteration + return other.offset <=> offset; + } + + [[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; +}; + +} // 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 = stl::allocator_traits; + static_assert(stl::is_same_v, "Invalid value type"); + using sparse_container_type = stl::vector>; + using packed_container_type = stl::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]] stl::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 stl::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); + stl::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() { + for(auto page_allocator{packed.get_allocator()}; auto &&page: sparse) { + if(page != nullptr) { + stl::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); + page = nullptr; + } + } + } + + void swap_at(const stl::size_t lhs, const stl::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)); + + stl::swap(from, to); + } + +private: + [[nodiscard]] virtual const void *get_at(const stl::size_t) const { + return nullptr; + } + + virtual void swap_or_move([[maybe_unused]] const stl::size_t lhs, [[maybe_unused]] const stl::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 entt A valid identifier for the element to pop. + */ + void swap_only(const Entity entt) { + ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch"); + const auto pos = index(entt); + bump(traits_type::next(entt)); + swap_at(pos, head -= (pos < head)); + } + + /** + * @brief Erases an entity from a sparse set. + * @param entt A valid identifier for the element to pop. + */ + void swap_and_pop(const Entity entt) { + ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch"); + auto &self = sparse_ref(entt); + const auto pos = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(pos, traits_type::to_integral(packed.back())); + packed[static_cast(pos)] = 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 entt A valid identifier for the element to pop. + */ + void in_place_pop(const Entity entt) { + ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch"); + const auto pos = entity_to_pos(stl::exchange(sparse_ref(entt), null)); + packed[pos] = traits_type::combine(static_cast(stl::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() { + if(!packed.empty()) { + // suboptimal with few entities, but exploits cache way more with many + for(auto &&elem: sparse) { + if(elem) { + for(size_type pos{}; pos < traits_type::page_size; ++pos) { + elem[pos] = null; + } + } + } + } + + 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(stl::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 = traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::ptrdiff_t; + /*! @brief Pointer type to contained entities. */ + using pointer = 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 = stl::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = stl::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{stl::move(other.sparse)}, + packed{stl::move(other.packed)}, + descriptor{other.descriptor}, + mode{other.mode}, + head{stl::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{stl::move(other.sparse), allocator}, + packed{stl::move(other.packed), allocator}, + descriptor{other.descriptor}, + mode{other.mode}, + head{stl::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 stl::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(); + other.reserve(len); + + for(size_type cnt{}; auto &&elem: stl::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] = stl::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 stl::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 stl::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(stl::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. + * + * @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. + */ + iterator push(stl::input_iterator auto first, stl::input_iterator auto 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(stl::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(stl::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(stl::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 = stl::exchange(head, max_size); + + for(; from && packed[from - 1u] == tombstone; --from) {} + + while(pos != max_size) { + if(const auto to = stl::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(), stl::move(compare), stl::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 = stl::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, stl::move(compare), stl::move(algo), stl::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; + } + + /** + * @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(stl::forward(value))); + } + +private: + sparse_container_type sparse; + packed_container_type packed; + const type_info *descriptor; + deletion_policy mode; + size_type head; +}; + +} // namespace entt + +#endif diff --git a/include/entt/entity/storage.hpp b/include/entt/entity/storage.hpp new file mode 100644 index 0000000..d12bf7d --- /dev/null +++ b/include/entt/entity/storage.hpp @@ -0,0 +1,1222 @@ +#ifndef ENTT_ENTITY_STORAGE_HPP +#define ENTT_ENTITY_STORAGE_HPP + +#include +#include "../config/config.h" +#include "../core/bit.hpp" +#include "../core/iterator.hpp" +#include "../core/memory.hpp" +#include "../core/type_info.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/memory.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "component.hpp" +#include "entity.hpp" +#include "fwd.hpp" +#include "sparse_set.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +class storage_iterator final { + template + friend class storage_iterator; + + using container_type = stl::remove_const_t; + using alloc_traits = stl::allocator_traits; + + using iterator_traits = stl::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 = iterator_traits::value_type; + using pointer = iterator_traits::pointer; + using reference = iterator_traits::reference; + using difference_type = iterator_traits::difference_type; + using iterator_category = stl::random_access_iterator_tag; + + constexpr storage_iterator() noexcept = default; + + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept + : payload{ref}, + offset{idx} {} + + template> Other> + requires stl::is_const_v + constexpr storage_iterator(const storage_iterator &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 stl::addressof(operator[](0)); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return operator[](0); + } + + template + [[nodiscard]] constexpr stl::ptrdiff_t operator-(const storage_iterator &other) const noexcept { + // intentionally reversed due to backward iteration + return other.offset - offset; + } + + template + [[nodiscard]] constexpr bool operator==(const storage_iterator &other) const noexcept { + return offset == other.offset; + } + + template + [[nodiscard]] constexpr auto operator<=>(const storage_iterator &other) const noexcept { + // intentionally reversed due to backward iteration + return other.offset <=> offset; + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + Container *payload; + difference_type offset; +}; + +template +class extended_storage_iterator final { + template + friend class extended_storage_iterator; + +public: + using iterator_type = It; + using value_type = decltype(stl::tuple_cat(stl::make_tuple(*stl::declval()), stl::forward_as_tuple(*stl::declval()...))); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::forward_iterator_tag; + + constexpr extended_storage_iterator() + : it{} {} + + constexpr extended_storage_iterator(iterator_type base, Other... other) + : it{base, other...} {} + + template + requires (!stl::same_as && ...) && (stl::constructible_from && ...) + constexpr extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} + + constexpr extended_storage_iterator &operator++() noexcept { + return ++stl::get(it), (++stl::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 {*stl::get(it), *stl::get(it)...}; + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return stl::get(it); + } + + template + [[nodiscard]] constexpr bool operator==(const extended_storage_iterator &other) const noexcept { + return stl::get<0>(it) == stl::get<0>(other.it); + } + +private: + stl::tuple it; +}; + +} // 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 = stl::allocator_traits; + static_assert(stl::is_same_v, "Invalid value type"); + using container_type = stl::vector>; + using underlying_type = basic_sparse_set>; + using underlying_iterator = underlying_type::basic_iterator; + using traits_type = component_traits; + + [[nodiscard]] auto &element_at(const stl::size_t pos) const { + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + auto assure_at_least(const stl::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 = stl::to_address(assure_at_least(static_cast(it.index()))); + entt::uninitialized_construct_using_allocator(elem, get_allocator(), stl::forward(args)...); + } + ENTT_CATCH { + base_type::pop(it, it + 1u); + ENTT_THROW; + } + + return it; + } + + void shrink_to_size(const stl::size_t sz) { + const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; + allocator_type allocator{get_allocator()}; + + if constexpr(!stl::is_trivially_destructible_v) { + 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, stl::addressof(element_at(pos))); + } + } else { + alloc_traits::destroy(allocator, stl::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 stl::size_t lhs, const stl::size_t rhs) { + using stl::swap; + swap(element_at(lhs), element_at(rhs)); + } + + void move_to(const stl::size_t lhs, const stl::size_t rhs) { + auto &elem = element_at(lhs); + allocator_type allocator{get_allocator()}; + entt::uninitialized_construct_using_allocator(stl::to_address(assure_at_least(rhs)), allocator, stl::move(elem)); + alloc_traits::destroy(allocator, stl::addressof(elem)); + } + +private: + [[nodiscard]] const void *get_at(const stl::size_t pos) const final { + return stl::addressof(element_at(pos)); + } + + void swap_or_move([[maybe_unused]] const stl::size_t from, [[maybe_unused]] const stl::size_t to) override { + static constexpr bool is_pinned_type = !(stl::is_move_constructible_v && stl::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, stl::addressof(elem)); + } else if constexpr(stl::is_trivially_destructible_v) { + elem = stl::move(element_at(base_type::size() - 1u)); + base_type::swap_and_pop(*first); + } else { + auto &other = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = stl::exchange(elem, stl::move(other)); + alloc_traits::destroy(allocator, stl::addressof(other)); + base_type::swap_and_pop(*first); + } + } + } + + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + if constexpr(stl::is_trivially_destructible_v) { + base_type::pop_all(); + } else { + 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, stl::addressof(element_at(static_cast(first.index())))); + } + } else { + base_type::swap_and_pop(*first); + alloc_traits::destroy(allocator, stl::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(stl::is_copy_constructible_v) { + return emplace_element(entt, force_back, *static_cast(value)); + } else { + return base_type::end(); + } + } else { + if constexpr(stl::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 = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::ptrdiff_t; + /*! @brief Pointer type to contained elements. */ + using pointer = container_type::pointer; + /*! @brief Constant pointer type to contained elements. */ + using const_pointer = 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 = stl::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = stl::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{stl::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{stl::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 stl::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 stl::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return stl::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 stl::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return stl::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(stl::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]] stl::tuple get_as_tuple(const entity_type entt) const noexcept { + return stl::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] stl::tuple get_as_tuple(const entity_type entt) noexcept { + return stl::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(stl::is_aggregate_v && (sizeof...(Args) != 0u || !stl::is_default_constructible_v)) { + const auto it = emplace_element(entt, false, Type{stl::forward(args)...}); + return element_at(static_cast(it.index())); + } else { + const auto it = emplace_element(entt, false, stl::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); + (stl::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. + * + * @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. + */ + iterator insert(stl::input_iterator auto first, stl::input_iterator auto 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 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 from An iterator to the first element of the range of objects. + * @return Iterator pointing to the first element inserted, if any. + */ + template + requires stl::same_as::value_type, value_type> + iterator insert(stl::input_iterator auto first, stl::input_iterator auto last, It 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 +requires (component_traits::page_size == 0u) +class basic_storage + : public basic_sparse_set::template rebind_alloc> { + using alloc_traits = stl::allocator_traits; + static_assert(stl::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 = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::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{stl::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 { + 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]] stl::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Invalid entity"); + return stl::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, const auto &...) { + 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"); + (stl::forward(func)(), ...); + } + + /** + * @brief Assigns entities to 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 insert(stl::input_iterator auto first, stl::input_iterator auto last, const auto &...) { + 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 = stl::allocator_traits; + static_assert(stl::is_same_v, "Invalid value type"); + using underlying_iterator = 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 = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::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=(stl::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 stl::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]] stl::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 stl::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 output 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 It> + 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"); + (stl::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 stl::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 stl::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 diff --git a/include/entt/entity/view.hpp b/include/entt/entity/view.hpp new file mode 100644 index 0000000..feb54c4 --- /dev/null +++ b/include/entt/entity/view.hpp @@ -0,0 +1,1142 @@ +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include "../config/config.h" +#include "../core/concepts.hpp" +#include "../core/iterator.hpp" +#include "../core/type_traits.hpp" +#include "../stl/array.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "entity.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +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 const Type placeholder{}; + return &placeholder; +} + +[[nodiscard]] bool all_of(auto first, const auto last, const auto entt) noexcept { + for(; (first != last) && (*first)->contains(entt); ++first) {} + return first == last; +} + +[[nodiscard]] bool none_of(auto first, const auto last, const auto entt) noexcept { + for(; (first != last) && !(*first)->contains(entt); ++first) {} + return first == last; +} + +template +[[nodiscard]] bool fully_initialized(It first, const It last, const stl::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, stl::index_sequence, stl::index_sequence, stl::index_sequence, stl::index_sequence) { + Result elem{}; + // friend-initialization, avoid multiple calls to refresh + elem.pools = {view.template storage()..., other.template storage()...}; + [[maybe_unused]] const 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 struct extended_view_iterator; + + using iterator_type = Type::const_iterator; + using iterator_traits = stl::iterator_traits; + + [[nodiscard]] bool valid(const 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 = iterator_traits::value_type; + using pointer = iterator_traits::pointer; + using reference = iterator_traits::reference; + using difference_type = iterator_traits::difference_type; + using iterator_category = stl::forward_iterator_tag; + + constexpr view_iterator() noexcept + : it{}, + pools{}, + filter{}, + index{} {} + + view_iterator(iterator_type first, stl::array value, stl::array excl, const stl::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 + [[nodiscard]] constexpr bool operator==(const view_iterator &other) const noexcept { + return it == other.it; + } + +private: + iterator_type it; + stl::array pools; + stl::array filter; + difference_type index; +}; + +template +struct extended_view_iterator final { + using iterator_type = It; + using value_type = decltype(stl::tuple_cat(stl::make_tuple(*stl::declval()), stl::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::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 [this](stl::index_sequence) { + return stl::tuple_cat(stl::make_tuple(*it), static_cast(const_cast *>(stl::get(it.pools)))->get_as_tuple(*it)...); + }(stl::index_sequence_for{}); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + [[nodiscard]] constexpr bool operator==(const extended_view_iterator &other) const noexcept { + return it == other.it; + } + +private: + It it; +}; + +} // 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 { + template + friend Return internal::view_pack(const View &, const Other &, stl::index_sequence, stl::index_sequence, stl::index_sequence, stl::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 ENTT_INTERNAL */ + basic_common_view() noexcept { + for(size_type pos{}, last = filter.size(); pos < last; ++pos) { + filter[pos] = placeholder; + } + } + + basic_common_view(stl::array value, stl::array excl) noexcept + : pools{value}, + filter{excl}, + index{Get} { + unchecked_refresh(); + } + + [[nodiscard]] const Type *pool_at(const stl::size_t pos) const noexcept { + return pools[pos]; + } + + void pool_at(const stl::size_t pos, const Type *elem) noexcept { + ENTT_ASSERT(elem != nullptr, "Unexpected element"); + pools[pos] = elem; + refresh(); + } + + [[nodiscard]] const Type *filter_at(const stl::size_t pos) const noexcept { + return (filter[pos] == placeholder) ? nullptr : filter[pos]; + } + + void filter_at(const stl::size_t pos, const Type *elem) noexcept { + ENTT_ASSERT(elem != nullptr, "Unexpected element"); + filter[pos] = elem; + } + + [[nodiscard]] bool none_of(const Type::entity_type entt) const noexcept { + return internal::none_of(filter.begin(), filter.end(), entt); + } + + void use(const stl::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 = Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::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: + stl::array pools{}; + stl::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 +requires (sizeof...(Get) != 0u) +class basic_view, exclude_t> + : 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 stl::size_t index_of = type_list_index_v, type_list>; + + template + [[nodiscard]] auto dispatch_get(const stl::tuple &curr) const { + if constexpr(Curr == Other) { + return stl::forward_as_tuple(stl::get(curr)...); + } else { + return storage()->get_as_tuple(stl::get<0>(curr)); + } + } + + template + void each(Func func, stl::index_sequence) const { + for(const auto curr: storage()->each()) { + if(const auto entt = stl::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{}, stl::declval().get({})))>) { + stl::apply(func, stl::tuple_cat(stl::make_tuple(entt), dispatch_get(curr)...)); + } else { + stl::apply(func, stl::tuple_cat(dispatch_get(curr)...)); + } + } + } + } + + template + void storage_if(Type *elem) noexcept { + if(elem != nullptr) { + storage>(*elem); + } + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = base_type::size_type; + /*! @brief Signed integer type. */ + using difference_type = stl::ptrdiff_t; + /*! @brief Forward iterator type. */ + using iterator = 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(stl::tuple value, stl::tuple excl = {}) noexcept + : basic_view{stl::make_from_tuple(stl::tuple_cat(value, excl))} {} + + /** + * @brief Constructs a view from a convertible counterpart. + * @tparam Args Storage types managed by the other view. + * @param other A view to convert from. + */ + template + requires (!stl::same_as>) + basic_view(const basic_view &other) noexcept + : basic_view{} { + (storage_if(other.template storage()), ...); + (storage_if(other.template storage()), ...); + } + + /** + * @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(stl::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 [this, entt](stl::index_sequence) { + return stl::tuple_cat(this->storage()->get_as_tuple(entt)...); + }(stl::index_sequence_for{}); + } else if constexpr(sizeof...(Index) == 1) { + return (storage()->get(entt), ...); + } else { + return stl::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 { + [this, &func](stl::index_sequence seq) { + if(const auto *view = base_type::handle(); view != nullptr) { + ((view == base_type::pool_at(Index) ? each(stl::move(func), seq) : void()), ...); + } + }(stl::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 OGet> + [[nodiscard]] 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... OGet, stl::derived_from... OExclude> + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, stl::index_sequence_for{}, stl::index_sequence_for{}, stl::index_sequence_for{}, stl::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 { +protected: + /*! @cond ENTT_INTERNAL */ + 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 = common_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = stl::size_t; + /*! @brief Signed integer type. */ + using difference_type = stl::ptrdiff_t; + /*! @brief Random access iterator type. */ + using iterator = stl::conditional_t, typename common_type::iterator>; + /*! @brief Reverse iterator type. */ + using reverse_iterator = stl::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. + * @return Number of entities that have the given element. + */ + [[nodiscard]] size_type size() const noexcept + requires (Policy != deletion_policy::in_place) { + 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. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const noexcept + requires (Policy == deletion_policy::in_place) { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept + requires (Policy != deletion_policy::in_place) { + 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()`. + * + * @return An iterator to the first entity of the reversed view. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept + requires (Policy != deletion_policy::in_place) { + return leading ? leading->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + [[nodiscard]] reverse_iterator rend() const noexcept + requires (Policy != deletion_policy::in_place) { + 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; + + void storage_if(Get *value) noexcept { + if(value != nullptr) { + storage(*value); + } + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = base_type::size_type; + /*! @brief Signed integer type. */ + using difference_type = stl::ptrdiff_t; + /*! @brief Random access iterator type. */ + using iterator = base_type::iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = base_type::reverse_iterator; + /*! @brief Iterable view type. */ + using iterable = stl::conditional_t>, decltype(stl::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(stl::tuple value, stl::tuple<> = {}) noexcept + : basic_view{stl::get<0>(value)} {} + + /** + * @brief Constructs a view from a convertible counterpart. + * @tparam Args Storage types managed by the other view. + * @param other A view to convert from. + */ + template + requires (!stl::same_as>) + basic_view(const basic_view &other) noexcept + : base_type{} { + storage_if(other.template storage()); + } + + /** + * @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(stl::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(stl::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(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{}, stl::declval().get({})))>) { + for(const auto pack: each()) { + stl::apply(func, pack); + } + } else if constexpr(Get::storage_policy == deletion_policy::swap_and_pop || Get::storage_policy == deletion_policy::swap_only) { + if constexpr(stl::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()) { + stl::apply([&func](const auto, auto &&...elem) { func(stl::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 OGet> + [[nodiscard]] 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... OGet, stl::derived_from... OExclude> + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, stl::index_sequence_for{}, stl::index_sequence_for<>{}, stl::index_sequence_for{}, stl::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(stl::tuple, stl::tuple = {}) -> basic_view, exclude_t>; + +} // namespace entt + +#endif diff --git a/include/entt/entt.hpp b/include/entt/entt.hpp new file mode 100644 index 0000000..09bd14f --- /dev/null +++ b/include/entt/entt.hpp @@ -0,0 +1,93 @@ +/*! @brief `EnTT` default namespace. */ +namespace entt {} + +/*! @brief Custom `EnTT` namespace for the standard template library. */ +namespace entt::stl {} + +// IWYU pragma: begin_exports +#include "config/config.h" +#include "config/macro.h" +#include "config/version.h" +#include "container/dense_map.hpp" +#include "container/dense_set.hpp" +#include "container/table.hpp" +#include "core/algorithm.hpp" +#include "core/any.hpp" +#include "core/bit.hpp" +#include "core/compressed_pair.hpp" +#include "core/concepts.hpp" +#include "core/enum.hpp" +#include "core/family.hpp" +#include "core/hashed_string.hpp" +#include "core/ident.hpp" +#include "core/iterator.hpp" +#include "core/memory.hpp" +#include "core/monostate.hpp" +#include "core/ranges.hpp" +#include "core/tuple.hpp" +#include "core/type_info.hpp" +#include "core/type_traits.hpp" +#include "core/utility.hpp" +#include "entity/component.hpp" +#include "entity/entity.hpp" +#include "entity/group.hpp" +#include "entity/handle.hpp" +#include "entity/helper.hpp" +#include "entity/mixin.hpp" +#include "entity/organizer.hpp" +#include "entity/ranges.hpp" +#include "entity/registry.hpp" +#include "entity/runtime_view.hpp" +#include "entity/snapshot.hpp" +#include "entity/sparse_set.hpp" +#include "entity/storage.hpp" +#include "entity/view.hpp" +#include "graph/adjacency_matrix.hpp" +#include "graph/dot.hpp" +#include "graph/flow.hpp" +#include "locator/locator.hpp" +#include "meta/adl_pointer.hpp" +#include "meta/container.hpp" +#include "meta/context.hpp" +#include "meta/factory.hpp" +#include "meta/meta.hpp" +#include "meta/node.hpp" +#include "meta/pointer.hpp" +#include "meta/policy.hpp" +#include "meta/range.hpp" +#include "meta/resolve.hpp" +#include "meta/template.hpp" +#include "meta/type_traits.hpp" +#include "meta/utility.hpp" +#include "poly/poly.hpp" +#include "process/process.hpp" +#include "process/scheduler.hpp" +#include "resource/cache.hpp" +#include "resource/loader.hpp" +#include "resource/resource.hpp" +#include "signal/delegate.hpp" +#include "signal/dispatcher.hpp" +#include "signal/emitter.hpp" +#include "signal/sigh.hpp" +#include "stl/algorithm.hpp" +#include "stl/array.hpp" +#include "stl/atomic.hpp" +#include "stl/bit.hpp" +#include "stl/cmath.hpp" +#include "stl/concepts.hpp" +#include "stl/cstddef.hpp" +#include "stl/cstdint.hpp" +#include "stl/functional.hpp" +#include "stl/ios.hpp" +#include "stl/iterator.hpp" +#include "stl/limits.hpp" +#include "stl/memory.hpp" +#include "stl/ostream.hpp" +#include "stl/sstream.hpp" +#include "stl/string.hpp" +#include "stl/string_view.hpp" +#include "stl/tuple.hpp" +#include "stl/type_traits.hpp" +#include "stl/utility.hpp" +#include "stl/vector.hpp" +// IWYU pragma: end_exports diff --git a/include/entt/fwd.hpp b/include/entt/fwd.hpp new file mode 100644 index 0000000..4b6e60c --- /dev/null +++ b/include/entt/fwd.hpp @@ -0,0 +1,11 @@ +// IWYU pragma: begin_exports +#include "container/fwd.hpp" +#include "core/fwd.hpp" +#include "entity/fwd.hpp" +#include "graph/fwd.hpp" +#include "meta/fwd.hpp" +#include "poly/fwd.hpp" +#include "process/fwd.hpp" +#include "resource/fwd.hpp" +#include "signal/fwd.hpp" +// IWYU pragma: end_exports diff --git a/include/entt/graph/adjacency_matrix.hpp b/include/entt/graph/adjacency_matrix.hpp new file mode 100644 index 0000000..ba83561 --- /dev/null +++ b/include/entt/graph/adjacency_matrix.hpp @@ -0,0 +1,332 @@ +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include "../config/config.h" +#include "../core/iterator.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +class edge_iterator { + using size_type = stl::size_t; + + void find_next() noexcept { + for(; pos != last && !it[static_cast(pos)]; pos += offset) {} + } + +public: + using value_type = stl::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::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{stl::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 stl::make_pair(pos / vert, pos % vert); + } + + [[nodiscard]] constexpr bool operator==(const edge_iterator &other) const noexcept { + return pos == other.pos; + } + +private: + It it{}; + size_type vert{}; + size_type pos{}; + size_type last{}; + size_type offset{}; +}; + +} // 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 Category, typename Allocator> +class adjacency_matrix { + using alloc_traits = stl::allocator_traits; + static_assert(stl::is_same_v, "Invalid value type"); + using container_type = stl::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = stl::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = stl::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{stl::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 stl::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. + */ + stl::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(stl::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 = !stl::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(stl::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return stl::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 diff --git a/include/entt/graph/dot.hpp b/include/entt/graph/dot.hpp new file mode 100644 index 0000000..e8d237a --- /dev/null +++ b/include/entt/graph/dot.hpp @@ -0,0 +1,56 @@ +#ifndef ENTT_GRAPH_DOT_HPP +#define ENTT_GRAPH_DOT_HPP + +#include "../stl/concepts.hpp" +#include "../stl/ostream.hpp" +#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. + * @param out A standard output stream. + * @param graph The graph to output. + * @param writer Vertex decorator object. + */ +template +requires stl::derived_from +void dot(stl::ostream &out, const Graph &graph, stl::invocable auto writer) { + if constexpr(stl::same_as) { + out << "graph{"; + } else { + out << "digraph{"; + } + + for(auto &&vertex: graph.vertices()) { + out << vertex << "["; + writer(out, vertex); + out << "];"; + } + + for(auto [lhs, rhs]: graph.edges()) { + if constexpr(stl::same_as) { + 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(stl::ostream &out, const Graph &graph) { + return dot(out, graph, [](auto &&...) {}); +} + +} // namespace entt + +#endif diff --git a/include/entt/graph/flow.hpp b/include/entt/graph/flow.hpp new file mode 100644 index 0000000..df4372f --- /dev/null +++ b/include/entt/graph/flow.hpp @@ -0,0 +1,345 @@ +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include "../config/config.h" +#include "../container/dense_map.hpp" +#include "../container/dense_set.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "../stl/algorithm.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/iterator.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "adjacency_matrix.hpp" +#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 = stl::allocator_traits; + static_assert(stl::is_same_v, "Invalid value type"); + using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; + using ro_rw_container_type = stl::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 = stl::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 = stl::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(stl::size_t vk{}; vk < length; ++vk) { + for(stl::size_t vi{}; vi < length; ++vi) { + for(stl::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(stl::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(stl::size_t vj{}; vj < length; ++vj) { + for(stl::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(stl::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 = stl::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{stl::move(other.vertices), allocator}, + deps{stl::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 stl::swap; + swap(index, other.index); + swap(vertices, other.vertices); + swap(deps, other.deps); + 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. + * @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. + */ + basic_flow &ro(stl::input_iterator auto first, stl::input_iterator auto 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. + * @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. + */ + basic_flow &rw(stl::input_iterator auto first, stl::input_iterator auto 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 diff --git a/include/entt/graph/fwd.hpp b/include/entt/graph/fwd.hpp new file mode 100644 index 0000000..c1fd58e --- /dev/null +++ b/include/entt/graph/fwd.hpp @@ -0,0 +1,28 @@ +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include "../core/fwd.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/memory.hpp" + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template, typename = stl::allocator> +class adjacency_matrix; + +template> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; + +} // namespace entt + +#endif diff --git a/include/entt/locator/locator.hpp b/include/entt/locator/locator.hpp new file mode 100644 index 0000000..0d6fecf --- /dev/null +++ b/include/entt/locator/locator.hpp @@ -0,0 +1,161 @@ +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + +#include "../config/config.h" +#include "../stl/concepts.hpp" +#include "../stl/memory.hpp" +#include "../stl/utility.hpp" + +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; + stl::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 Type = Service, typename... Args> + requires stl::constructible_from + [[nodiscard]] static Service &value_or(Args &&...args) { + return service ? *service : emplace(stl::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 Type = Service, typename... Args> + requires stl::constructible_from + static Service &emplace(Args &&...args) { + service = stl::make_shared(stl::forward(args)...); + return *service; + } + + /** + * @brief Sets or replaces a service using a given allocator. + * @tparam Type Service type. + * @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 Type = Service, typename... Args> + requires stl::constructible_from + static Service &emplace(stl::allocator_arg_t, auto alloc, Args &&...args) { + service = stl::allocate_shared(alloc, stl::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 Type, typename Deleter = stl::default_delete> + static void reset(Type *elem, Deleter deleter = {}) { + service = stl::shared_ptr{elem, stl::move(deleter)}; + } + +private: + // stl::shared_ptr because of its type erased allocator which is useful here + // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) + inline static stl::shared_ptr service{}; +}; + +} // namespace entt + +#endif diff --git a/include/entt/meta/adl_pointer.hpp b/include/entt/meta/adl_pointer.hpp new file mode 100644 index 0000000..5bb768a --- /dev/null +++ b/include/entt/meta/adl_pointer.hpp @@ -0,0 +1,35 @@ +#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 diff --git a/include/entt/meta/container.hpp b/include/entt/meta/container.hpp new file mode 100644 index 0000000..9c131be --- /dev/null +++ b/include/entt/meta/container.hpp @@ -0,0 +1,298 @@ +// IWYU pragma: always_keep + +#ifndef ENTT_META_CONTAINER_HPP +#define ENTT_META_CONTAINER_HPP + +#include "../core/concepts.hpp" +#include "../core/type_traits.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "context.hpp" +#include "fwd.hpp" +#include "meta.hpp" +#include "type_traits.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct sequence_container_extent: integral_constant {}; + +template +requires is_complete_v> +struct sequence_container_extent: integral_constant> {}; + +template +inline constexpr stl::size_t sequence_container_extent_v = sequence_container_extent::value; + +template +concept meta_sequence_container_like = requires(Type elem) { + typename Type::value_type; + typename Type::iterator; + requires entt::stl::forward_iterator; + { elem.begin() } -> stl::same_as; + { elem.end() } -> stl::same_as; + requires !requires { typename Type::key_type; }; + requires !requires { elem.substr(); }; +}; + +template +concept meta_associative_container_like = requires(Type value) { + typename Type::key_type; + typename Type::value_type; + typename Type::iterator; + requires entt::stl::forward_iterator; + { value.begin() } -> stl::same_as; + { value.end() } -> stl::same_as; + value.find(stl::declval()); +}; + +} // 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 { + /*! @brief Unsigned integer type. */ + using size_type = meta_sequence_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = meta_sequence_container::iterator; + + /*! @brief Number of elements, or `meta_dynamic_extent` if dynamic. */ + static constexpr stl::size_t extent = internal::sequence_container_extent_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([[maybe_unused]] void *container) { + if constexpr(requires(Type elem) { elem.clear(); }) { + 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(requires(Type elem) { elem.reserve(sz); }) { + 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(stl::is_default_constructible_v && requires(Type elem) { elem.resize(sz); }) { + 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(requires(Type elem, typename Type::const_iterator iter, Type::value_type instance) { elem.insert(iter, instance); }) { + 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(requires(Type elem, typename Type::const_iterator iter) { elem.erase(iter); }) { + 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 { + /*! @brief Unsigned integer type. */ + using size_type = meta_associative_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = meta_associative_container::iterator; + + /*! @brief True in case of key-only containers, false otherwise. */ + static constexpr bool key_only = !requires { typename Type::mapped_type; }; + + /** + * @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(requires(Type elem) { elem.reserve(sz); }) { + 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, stl::bool_constant{}, end ? static_cast(as_const)->cend() : static_cast(as_const)->cbegin()} + : iterator{area, stl::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, stl::bool_constant{}, static_cast(container)->find(*static_cast(key))} + : iterator{area, stl::bool_constant{}, static_cast(as_const)->find(*static_cast(key))}; + } +}; + +/** + * @brief Traits meta sequence container like types. + * @tparam Type Container type to inspect. + */ +template +struct meta_sequence_container_traits: basic_meta_sequence_container_traits {}; + +/** + * @brief Traits for meta associative container like types. + * @tparam Type Container type to inspect. + */ +template +struct meta_associative_container_traits: basic_meta_associative_container_traits {}; + +} // namespace entt + +#endif diff --git a/include/entt/meta/context.hpp b/include/entt/meta/context.hpp new file mode 100644 index 0000000..4d3e34e --- /dev/null +++ b/include/entt/meta/context.hpp @@ -0,0 +1,47 @@ +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +#include "../container/dense_map.hpp" +#include "../core/fwd.hpp" +#include "../stl/functional.hpp" +#include "../stl/memory.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +struct meta_type_node; + +struct meta_context { + using bucket_type = dense_map, stl::identity>; + + bucket_type bucket; + + [[nodiscard]] inline static meta_context &from(meta_ctx &); + [[nodiscard]] inline static const meta_context &from(const meta_ctx &); +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Opaque meta context type. */ +struct meta_ctx: private internal::meta_context { + // attorney idiom like model to access the base class + friend struct internal::meta_context; +}; + +/*! @cond ENTT_INTERNAL */ +[[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 diff --git a/include/entt/meta/factory.hpp b/include/entt/meta/factory.hpp new file mode 100644 index 0000000..e91888a --- /dev/null +++ b/include/entt/meta/factory.hpp @@ -0,0 +1,656 @@ +#ifndef ENTT_META_FACTORY_HPP +#define ENTT_META_FACTORY_HPP + +#include "../config/config.h" +#include "../core/bit.hpp" +#include "../core/fwd.hpp" +#include "../core/hashed_string.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../locator/locator.hpp" +#include "../stl/algorithm.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/cstdint.hpp" +#include "../stl/functional.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "context.hpp" +#include "fwd.hpp" +#include "meta.hpp" +#include "node.hpp" +#include "policy.hpp" +#include "range.hpp" +#include "utility.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +class basic_meta_factory { + using invoke_type = stl::remove_pointer_t; + + enum class mode { + type, + data, + func + }; + + [[nodiscard]] auto *find_member_or_assert() { + auto *member = find_member(parent->details->data, bucket); + ENTT_ASSERT(member != nullptr, "Cannot find member"); + return member; + } + + [[nodiscard]] auto *find_overload_or_assert() { + ENTT_ASSERT(invoke != nullptr, "Invoke function not available"); + auto *overload = find_overload(find_member(parent->details->func, bucket), invoke); + ENTT_ASSERT(overload != nullptr, "Cannot find overload"); + return overload; + } + + bool unique_alias(const id_type alias) const noexcept { + return (ctx->bucket.find(alias) == ctx->bucket.cend()) && (stl::find_if(ctx->bucket.cbegin(), ctx->bucket.cend(), [alias](const auto &value) { return value.second->alias == alias; }) == ctx->bucket.cend()); + } + +protected: + void type(const id_type alias, const char *name) noexcept { + state = mode::type; + ENTT_ASSERT((parent->alias == alias) || unique_alias(alias), "Duplicate identifier"); + parent->alias = alias; + parent->name = name; + } + + template + void insert_or_assign(Type node) { + state = mode::type; + + if constexpr(stl::is_same_v) { + auto *member = find_member(parent->details->base, node.id); + member ? (*member = node) : parent->details->base.emplace_back(node); + } else if constexpr(stl::is_same_v) { + auto *member = find_member(parent->details->conv, node.id); + member ? (*member = node) : parent->details->conv.emplace_back(node); + } else { + static_assert(stl::is_same_v, "Unexpected type"); + auto *member = find_member(parent->details->ctor, node.id); + member ? (*member = node) : parent->details->ctor.emplace_back(node); + } + } + + void data(meta_data_node node) { + state = mode::data; + bucket = node.id; + + if(auto *member = find_member(parent->details->data, node.id); member == nullptr) { + parent->details->data.emplace_back(stl::move(node)); + } else if(member->set != node.set || member->get != node.get) { + *member = stl::move(node); + } + } + + void func(meta_func_node node) { + state = mode::func; + bucket = node.id; + invoke = node.invoke; + + if(auto *member = find_member(parent->details->func, node.id); member == nullptr) { + parent->details->func.emplace_back(stl::move(node)); + } else if(auto *overload = find_overload(member, node.invoke); overload == nullptr) { + while(member->next != nullptr) { member = member->next.get(); } + member->next = stl::make_unique(stl::move(node)); + } + } + + void traits(const meta_traits value, const bool unset) { + const auto set_or_unset_on = [=](auto &node) { + node.traits = (unset ? (node.traits & ~value) : (node.traits | value)); + }; + + switch(state) { + case mode::type: + set_or_unset_on(*parent); + break; + case mode::data: + set_or_unset_on(*find_member_or_assert()); + break; + case mode::func: + set_or_unset_on(*find_overload_or_assert()); + break; + } + } + + void custom(meta_custom_node node) { + switch(state) { + case mode::type: + parent->custom = stl::move(node); + break; + case mode::data: + find_member_or_assert()->custom = stl::move(node); + break; + case mode::func: + find_overload_or_assert()->custom = stl::move(node); + break; + } + } + +public: + basic_meta_factory(meta_ctx &area, meta_type_node node, const id_type id) + : ctx{&meta_context::from(area)}, + bucket{}, + state{mode::type} { + if(const auto it = ctx->bucket.find(id); it == ctx->bucket.cend()) { + ENTT_ASSERT(unique_alias(id), "Duplicate identifier"); + parent = ctx->bucket.emplace(id, stl::make_unique(stl::move(node))).first->second.get(); + parent->details = stl::make_unique(); + parent->alias = id; + } else { + parent = it->second.get(); + } + } + +private: + meta_context *ctx{}; + invoke_type *invoke{}; + meta_type_node *parent{}; + id_type bucket{}; + mode state{}; +}; + +} // 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 + : base_type{area, internal::setup_node_for(), type_hash::value()} {} + + /** + * @brief Constructs an unconstrained type assigned to a given identifier. + * @param id A custom unique identifier. + */ + meta_factory(const id_type id) noexcept + : meta_factory{locator::value_or(), id} {} + + /** + * @brief Context aware constructor. + * @param id A custom unique identifier. + * @param area The context into which to construct meta types. + */ + meta_factory(meta_ctx &area, const id_type id) noexcept + : base_type{area, internal::setup_node_for(), id} {} + + /** + * @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 alias 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 alias, const char *name = nullptr) noexcept { + base_type::type(alias, 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 + requires stl::derived_from + meta_factory base() noexcept { + if constexpr(!stl::same_as) { + 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 = stl::remove_cvref_t>; + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, stl::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 = stl::remove_cvref_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(stl::is_same_v, element_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(stl::is_member_object_pointer_v) { + using data_type = stl::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 */ + stl::is_const_v> ? internal::meta_traits::is_const : internal::meta_traits::is_none, + 1u, + 0u, + &meta_arg>>, + &meta_arg>, + &internal::resolve>, + &meta_setter, + &meta_getter}); + } else { + using data_type = stl::remove_pointer_t; + + if constexpr(stl::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, + ((!stl::is_pointer_v || stl::is_const_v) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, + 1u, + 0u, + &meta_arg>>, + &meta_arg>, + &internal::resolve>, + &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 getter = meta_function_helper_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + if constexpr(stl::is_same_v) { + base_type::data( + internal::meta_data_node{ + id, + name, + /* this is never static */ + internal::meta_traits::is_const, + 0u, + getter::args_type::size, + &meta_arg>, + &meta_arg, + &internal::resolve>, + &meta_setter, + &meta_getter}); + } else { + using setter = meta_function_helper_t; + + base_type::data( + internal::meta_data_node{ + id, + name, + /* this is never static nor const */ + internal::meta_traits::is_none, + setter::args_type::size, + getter::args_type::size, + &meta_arg, + &meta_arg, + &internal::resolve>, + &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, stl::remove_cvref_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(stl::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(), stl::make_shared(stl::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 alias Unique identifier. + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx, const id_type alias) noexcept { + auto &bucket = internal::meta_context::from(ctx).bucket; + + // fast path for unsearchable and overloaded types + if(bucket.erase(alias) == 0u) { + if(const auto it = stl::find_if(bucket.cbegin(), bucket.cend(), [alias](const auto &value) { return value.second->alias == alias; }); it != bucket.cend()) { + bucket.erase(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 alias Unique identifier. + */ +inline void meta_reset(const id_type alias) noexcept { + meta_reset(locator::value_or(), alias); +} + +/** + * @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).bucket.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).bucket.clear(); +} + +/** + * @brief Resets all meta types. + * + * @sa meta_reset + */ +inline void meta_reset() noexcept { + meta_reset(locator::value_or()); +} + +} // namespace entt + +#endif diff --git a/include/entt/meta/fwd.hpp b/include/entt/meta/fwd.hpp new file mode 100644 index 0000000..ac949c7 --- /dev/null +++ b/include/entt/meta/fwd.hpp @@ -0,0 +1,43 @@ +#ifndef ENTT_META_FWD_HPP +#define ENTT_META_FWD_HPP + +#include "../stl/cstddef.hpp" +#include "../stl/limits.hpp" + +namespace entt { + +struct meta_ctx; + +class meta_sequence_container; + +class meta_associative_container; + +class meta_any; + +class meta_handle; + +struct meta_custom; + +struct meta_data; + +struct meta_func; + +struct meta_base; + +class meta_type; + +template +class meta_factory; + +/*! @brief Used to identicate that a sequence container has not a fixed size. */ +inline constexpr stl::size_t meta_dynamic_extent = (stl::numeric_limits::max)(); + +/*! @brief Disambiguation tag for constructors and the like. */ +struct 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{}; + +} // namespace entt + +#endif diff --git a/include/entt/meta/meta.hpp b/include/entt/meta/meta.hpp new file mode 100644 index 0000000..f94026d --- /dev/null +++ b/include/entt/meta/meta.hpp @@ -0,0 +1,1902 @@ +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + +#include "../config/config.h" +#include "../core/any.hpp" +#include "../core/concepts.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 "../stl/array.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/memory.hpp" +#include "../stl/string_view.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "adl_pointer.hpp" +#include "context.hpp" +#include "fwd.hpp" +#include "node.hpp" +#include "range.hpp" +#include "type_traits.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +struct basic_meta_object { + [[nodiscard]] auto &node_or_assert() const noexcept { + ENTT_ASSERT(node != nullptr, "Invalid pointer to node"); + return *node; + } + + const Type *node{}; + const meta_ctx *ctx{&locator::value_or()}; +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = stl::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{stl::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 = stl::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{stl::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 &, void *); + + template + static void basic_vtable(const internal::meta_traits req, const meta_any &value, [[maybe_unused]] void *other) { + 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) { + if constexpr(!stl::is_void_v::element_type>>) { + if constexpr(stl::is_constructible_v) { + if(const auto &pointer_like = any_cast(value.storage); pointer_like) { + static_cast(other)->emplace::dereference(stl::declval()))>(adl_meta_pointer_like::dereference(pointer_like)); + } + } else { + static_cast(other)->emplace::dereference(stl::declval()))>(adl_meta_pointer_like::dereference(any_cast(value.storage))); + } + } + } + } else if constexpr(requires(Type elem) { *elem; }) { + if(req == internal::meta_traits::is_pointer) { + if constexpr(stl::is_class_v) { + if(const auto &elem = any_cast(value.storage); elem) { + return (value.storage.policy() == any_policy::cref) ? static_cast(other)->emplace(*elem) : static_cast(other)->emplace(elem))>(*const_cast(elem)); + } + } else if constexpr(!stl::is_array_v && !stl::is_void_v>>) { + if(auto *pointer = any_cast(value.storage); pointer) { + static_cast(other)->emplace>>, Type, stl::remove_pointer_t &>>(*pointer); + } + } + } + } else 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 = stl::conditional_t>, meta_sequence_container, meta_associative_container>; + *static_cast(other) = (value.storage.policy() == any_policy::cref) ? container_type{*value.ctx, any_cast(value.storage)} : 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{stl::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. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(stl::in_place_type_t, auto &&...args) + : meta_any{locator::value_or(), stl::in_place_type, stl::forward(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @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, stl::in_place_type_t, auto &&...args) + : storage{stl::in_place_type, stl::forward(args)...}, + ctx{&area}, + vtable{&basic_vtable>} {} + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @param value A pointer to an object to take ownership of. + */ + explicit meta_any(stl::in_place_t, auto *value) + : meta_any{locator::value_or(), stl::in_place, value} {} + + /** + * @brief Constructs a wrapper taking ownership of the passed object. + * @param area The context from which to search for meta types. + * @param value A pointer to an object to take ownership of. + */ + explicit meta_any(const meta_ctx &area, stl::in_place_t, auto *value) + : storage{stl::in_place, value}, + ctx{&area}, + vtable{storage ? &basic_vtable>> : nullptr} { + } + + /** + * @brief Constructs a wrapper from a given value. + * @param value An instance of an object to use to initialize the wrapper. + */ + meta_any(auto &&value) + requires (!stl::same_as, meta_any>) + : meta_any{locator::value_or(), stl::forward(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + meta_any(const meta_ctx &area, auto &&value) + requires (!stl::same_as, meta_any>) + : meta_any{area, stl::in_place_type>, stl::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{stl::move(other.storage)}, + ctx{&area}, + node{(ctx == other.ctx) ? stl::exchange(other.node, nullptr) : nullptr}, + vtable{stl::exchange(other.vtable, nullptr)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) + : storage{other.storage}, + ctx{other.ctx}, + node{(other.storage && !storage) ? nullptr : other.node}, + vtable{(other.storage && !storage) ? nullptr : other.vtable} { + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + meta_any(meta_any &&other) noexcept + : storage{stl::move(other.storage)}, + ctx{other.ctx}, + node{stl::exchange(other.node, nullptr)}, + vtable{stl::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) { + ctx = other.ctx; + storage = other.storage; + node = (other.storage && !storage) ? nullptr : other.node; + vtable = (other.storage && !storage) ? nullptr : 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 = stl::move(other.storage); + ctx = other.ctx; + node = stl::exchange(other.node, nullptr); + vtable = stl::exchange(other.vtable, nullptr); + return *this; + } + + /** + * @brief Value assignment operator. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ + meta_any &operator=(auto &&value) + requires (!stl::same_as, meta_any>) { + emplace>(stl::forward(value)); + return *this; + } + + /** + * @brief Returns the meta type associated with the contained instance. + * @return The meta type associated with the contained instance. + */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Sets a meta type for the contained instance. + * @param alias The meta to use with the contained instance. + */ + inline void type(const meta_type &alias) noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(id_type id, auto &&...args) const; + + /*! @copydoc invoke */ + meta_any invoke(id_type id, auto &&...args); + + /** + * @brief Sets the value of a given variable. + * @param id Unique identifier. + * @param args Parameters to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + bool set(id_type id, auto &&...args); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @param args Parameters to use to set the underlying variable, if any. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(id_type id, auto &&...args) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(id_type id, auto &&...args); + + /** + * @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) && !stl::is_const_v) ? nullptr : const_cast(stl::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]] stl::remove_const_t cast() const { + auto *const instance = try_cast>(); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); + } + + /*! @copydoc cast */ + template + [[nodiscard]] stl::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(!stl::is_reference_v || stl::is_const_v>) { + if(storage.has_value>()) { + return as_ref(); + } else if(*this) { + if constexpr(stl::is_arithmetic_v> || stl::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(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.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.id == entt::type_hash>::value()) { + return other; + } else if(auto from_base = stl::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(stl::is_reference_v && !stl::is_const_v>) { + return allow_cast &>() && (storage.policy() != any_policy::cref); + } else { + if(storage.has_value>()) { + return true; + } else if(auto other = stl::as_const(*this).allow_cast>(); other) { + if(other.storage.owner()) { + stl::swap(*this, other); + } + + return true; + } + + return false; + } + } + + /*! @copydoc any::emplace */ + template + void emplace(auto &&...args) { + storage.emplace(stl::forward(args)...); + auto *prev = stl::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, as_ref(), &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, as_ref(), &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*() noexcept { + meta_any ret{meta_ctx_arg, *ctx}; + if(*this) { vtable(internal::meta_traits::is_pointer, *this, &ret); } + return ret; + } + + /*! @copydoc operator* */ + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; + if(*this) { vtable(internal::meta_traits::is_pointer, as_ref(), &ret); } + 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::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. + * @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. + */ +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, auto &&value) { + return meta_any{ctx, stl::in_place_type, stl::forward(value)}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +[[nodiscard]] meta_any forward_as_meta(auto &&value) { + return forward_as_meta(locator::value_or(), stl::forward(value)); +} + +/*! @brief Opaque pointers to instances of any type. */ +class meta_handle { + meta_handle(int, auto &value, auto &&...args) + requires stl::same_as, meta_any> + : any{stl::forward(args)..., value.as_ref()} {} + + meta_handle(char, auto &value, auto &&...args) + : any{stl::forward(args)..., stl::in_place_type, value} {} + +public: + /*! Default constructor. */ + meta_handle() = default; + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(const meta_ctx &ctx, auto &value) + requires (!stl::same_as, meta_handle>) + : meta_handle{0, value, ctx} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(auto &value) + requires (!stl::same_as, meta_handle>) + : 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, stl::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; + } + +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->id)) ? 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 Common opaque wrapper for meta objects. + * @tparam Type Underlying meta node type. + */ +template +struct meta_object: protected internal::basic_meta_object { + /*! @brief Underlying meta node type. */ + using node_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = stl::size_t; + + /*! @brief Default constructor. */ + meta_object() 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_object(const meta_ctx &area, const node_type &curr) noexcept + : internal::basic_meta_object{&curr, &area} { + } + + /** + * @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 (this->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_object &other) const noexcept { + return (this->ctx == other.ctx) && (this->node == other.node); + } +}; + +/*! @brief Opaque wrapper for data members. */ +struct meta_data: meta_object { + using meta_object::meta_object; + + /** + * @brief Returns the name assigned to a data member, if any. + * @return The name assigned to the data member, if any. + */ + [[nodiscard]] stl::string_view name() const noexcept { + return (node_or_assert().name == nullptr) ? stl::string_view{} : stl::string_view{node_or_assert().name}; + } + + /** + * @brief Returns the number of arguments of a data member's setter. + * @return The number of arguments accepted by the data member's setter. + */ + [[nodiscard]] size_type set_arity() const noexcept { + return node_or_assert().set_arity; + } + + /** + * @brief Returns the number of arguments of a data member's getter. + * @return The number of arguments accepted by the data member's getter. + */ + [[nodiscard]] size_type get_arity() const noexcept { + return node_or_assert().get_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. + * @param instance An instance that fits the underlying type. + * @param args Parameters to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + bool set(Instance &&instance, auto &&...args) const { + return (sizeof...(args) >= set_arity()) && node_or_assert().set(meta_handle{*ctx, stl::forward(instance)}, stl::array{meta_any{*ctx, stl::forward(args)}...}.data()); + } + + /** + * @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. + * @param args Parameters to use to get the underlying variable, if any. + * @return A wrapper containing the value of the underlying variable. + */ + template + [[nodiscard]] meta_any get(Instance &&instance, auto &&...args) const { + return (sizeof...(args) >= get_arity()) ? node_or_assert().get(meta_handle{*ctx, stl::forward(instance)}, stl::array{meta_any{*ctx, stl::forward(args)}...}.data()) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns the type of the i-th argument of a data member's setter. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a data member's setter. + */ + [[nodiscard]] inline meta_type set_arg(size_type index) const noexcept; + + /** + * @brief Returns the type of the i-th argument of a data member's getter. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a data member's getter. + */ + [[nodiscard]] inline meta_type get_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 Opaque wrapper for member functions. */ +struct meta_func: meta_object { + using meta_object::meta_object; + + /** + * @brief Returns the name assigned to a member function, if any. + * @return The name assigned to the member function, if any. + */ + [[nodiscard]] stl::string_view name() const noexcept { + return (node_or_assert().name == nullptr) ? stl::string_view{} : stl::string_view{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. + * @return A wrapper containing the returned value, if any. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(Instance &&instance, auto &&...args) const { + return (sizeof...(args) == arity()) ? node_or_assert().invoke(meta_handle{*ctx, stl::forward(instance)}, stl::array{meta_any{*ctx, stl::forward(args)}...}.data()) : meta_any{meta_ctx_arg, *ctx}; + } + + /*! @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{}; + } +}; + +/*! @brief Opaque wrapper for base types. */ +struct meta_base: meta_object { + using meta_object::meta_object; + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const noexcept; +}; + +/*! @brief Opaque wrapper for types. */ +class meta_type { + friend class meta_any; + + [[nodiscard]] const auto &fetch_node() const { + return (node == nullptr) ? internal::resolve(internal::meta_context::from(*ctx)) : *node; + } + + [[nodiscard]] auto lookup(meta_handle *const args, const auto sz, [[maybe_unused]] bool constness, auto next) const { + decltype(next()) candidate = nullptr; + size_type same{}; + bool ambiguous{}; + + for(auto curr = next(); curr; curr = next()) { + if constexpr(stl::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 stl::span) + for(; pos < sz; ++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(type.fetch_node().details->base, info.hash()) || internal::find_member(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(stl::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 = 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 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 alias assigned to a type. + * @return The alias assigned to the type. + */ + [[nodiscard]] id_type alias() const noexcept { + return fetch_node().alias; + } + + /** + * @brief Returns the name assigned to a type, if any. + * @return The name assigned to the type, if any. + */ + [[nodiscard]] stl::string_view name() const noexcept { + return (fetch_node().name == nullptr) ? stl::string_view{} : stl::string_view{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(from.details->conv, to); elem != nullptr) { + return true; + } + + for(auto &&curr: from.details->base) { + if(curr.id == to || meta_type{*ctx, curr.type(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{}; + } + + /** + * @copybrief construct + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(auto &&...args) const { + if(const auto &ref = fetch_node(); ref.details) { + if(const auto *candidate = lookup(stl::array{meta_handle{*ctx, args}...}.data(), sizeof...(args), false, [first = ref.details->ctor.cbegin(), last = ref.details->ctor.cend()]() mutable { return first == last ? nullptr : &*(first++); }); candidate) { + return candidate->invoke(*ctx, stl::array{meta_any{*ctx, stl::forward(args)}...}.data()); + } + } + + if(const auto &ref = fetch_node(); (sizeof...(args) == 0u) && (ref.default_constructor != nullptr)) { + return ref.default_constructor(*ctx); + } + + return 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. + * @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}; + } + + /** + * @copybrief invoke + * @param id Unique identifier. + * @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. + * @return A wrapper containing the returned value, if any. + */ + template + // NOLINTNEXTLINE(modernize-use-nodiscard) + meta_any invoke(const id_type id, Instance &&instance, auto &&...args) const { + meta_handle wrapped{*ctx, stl::forward(instance)}; + + if(const auto &ref = fetch_node(); ref.details) { + if(auto *elem = internal::find_member(ref.details->func, id); elem != nullptr) { + if(const auto *candidate = lookup(stl::array{meta_handle{*ctx, args}...}.data(), sizeof...(args), (wrapped->base().policy() == any_policy::cref), [curr = elem]() mutable { return (curr != nullptr) ? stl::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + return candidate->invoke(stl::move(wrapped), stl::array{meta_any{*ctx, stl::forward(args)}...}.data()); + } + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.type().invoke(id, *wrapped.operator->(), stl::forward(args)...); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Sets 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. + * @param args Parameters 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, auto &&...args) const { + const auto candidate = data(id); + return candidate && candidate.set(stl::forward(instance), stl::forward(args)...); + } + + /** + * @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. + * @param args Parameters to use to set the underlying variable, if any. + * @return A wrapper containing the value of the underlying variable. + */ + template + [[nodiscard]] meta_any get(const id_type id, Instance &&instance, auto &&...args) const { + const auto candidate = data(id); + return candidate ? candidate.get(stl::forward(instance), stl::forward(args)...) : 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().alias == other.fetch_node().alias); + } + +private: + mutable const internal::meta_type_node *node{}; + const meta_ctx *ctx{&locator::value_or()}; +}; + +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return *this ? meta_type{*ctx, fetch_node()} : meta_type{}; +} + +inline void meta_any::type(const meta_type &alias) noexcept { + ENTT_ASSERT(storage.info() == alias.info(), "Unexpected type"); + node = alias.node; + ctx = alias.ctx; +} + +// NOLINTNEXTLINE(modernize-use-nodiscard) +meta_any meta_any::invoke(const id_type id, auto &&...args) const { + return type().invoke(id, *this, stl::forward(args)...); +} + +meta_any meta_any::invoke(const id_type id, auto &&...args) { + return type().invoke(id, *this, stl::forward(args)...); +} + +bool meta_any::set(const id_type id, auto &&...args) { + return type().set(id, *this, stl::forward(args)...); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id, auto &&...args) const { + return type().get(id, *this, stl::forward(args)...); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id, auto &&...args) { + return type().get(id, *this, stl::forward(args)...); +} + +[[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(from.details->conv, type.info().hash()); elem != nullptr) { + return elem->conv(*ctx, storage.data()); + } + + for(auto &&curr: from.details->base) { + if(auto other = curr.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.id == type.info().hash()) { + return other; + } else if(auto from_base = stl::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 = stl::as_const(*this).allow_cast(type); other) { + if(other.storage.owner()) { + stl::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(stl::move(other.storage)) || storage.assign(stl::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::set_arg(const size_type index) const noexcept { + return index < set_arity() ? node_or_assert().set_arg(*ctx, index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_data::get_arg(const size_type index) const noexcept { + return index < get_arity() ? node_or_assert().get_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{}; +} + +[[nodiscard]] inline meta_type meta_base::type() const noexcept { + return meta_type{*ctx, node_or_assert().type(internal::meta_context::from(*ctx))}; +} + +/*! @cond ENTT_INTERNAL */ +class meta_sequence_container::meta_iterator final { + using vtable_type = void(const void *, const stl::ptrdiff_t, meta_any *); + + template + static void basic_vtable(const void *value, const stl::ptrdiff_t offset, meta_any *other) { + const auto &it = *static_cast(value); + other ? other->emplace(*it) : stl::advance(const_cast(it), offset); + } + +public: + using value_type = meta_any; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::bidirectional_iterator_tag; + + meta_iterator() = default; + + meta_iterator(const meta_ctx &area, stl::bidirectional_iterator auto 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{}; +}; + +class meta_associative_container::meta_iterator final { + using vtable_type = void(const void *, stl::pair *); + + template + static void basic_vtable(const void *value, stl::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 = stl::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::forward_iterator_tag; + + meta_iterator() = default; + + template + meta_iterator(const meta_ctx &area, stl::bool_constant, stl::forward_iterator auto 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{}; +}; + +/*! @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 stl::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 diff --git a/include/entt/meta/node.hpp b/include/entt/meta/node.hpp new file mode 100644 index 0000000..8b35546 --- /dev/null +++ b/include/entt/meta/node.hpp @@ -0,0 +1,287 @@ +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include "../config/config.h" +#include "../core/bit.hpp" +#include "../core/concepts.hpp" +#include "../core/enum.hpp" +#include "../core/fwd.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../core/utility.hpp" +#include "../stl/array.hpp" +#include "../stl/bit.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/cstdint.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "context.hpp" +#include "fwd.hpp" +#include "type_traits.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +enum class meta_traits : stl::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 +requires stl::is_enum_v +[[nodiscard]] auto meta_to_user_traits(const meta_traits traits) noexcept { + constexpr auto shift = stl::popcount(static_cast>(meta_traits::_user_defined_traits)); + return Type{static_cast>(static_cast>(traits) >> shift)}; +} + +template +requires stl::is_enum_v +[[nodiscard]] auto user_to_meta_traits(const Type value) noexcept { + constexpr auto shift = stl::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 id{}; + stl::shared_ptr value{}; +}; + +struct meta_base_node { + id_type id{}; + const meta_type_node &(*type)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; +}; + +struct meta_conv_node { + id_type id{}; + meta_any (*conv)(const meta_ctx &, const void *){}; +}; + +struct meta_ctor_node { + using size_type = stl::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 = stl::size_t; + + id_type id{}; + const char *name{}; + meta_traits traits{meta_traits::is_none}; + size_type set_arity{0u}; + size_type get_arity{0u}; + meta_type (*set_arg)(const meta_ctx &, const size_type) noexcept {}; + meta_type (*get_arg)(const meta_ctx &, const size_type) noexcept {}; + const meta_type_node &(*type)(const meta_context &) noexcept {}; + bool (*set)(meta_handle, meta_any *const){}; + meta_any (*get)(meta_handle, meta_any *const){}; + meta_custom_node custom{}; +}; + +struct meta_func_node { + using size_type = stl::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){}; + stl::unique_ptr next; + meta_custom_node custom{}; +}; + +struct meta_template_node { + using size_type = stl::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 { + stl::vector ctor{}; + stl::vector base{}; + stl::vector conv{}; + stl::vector data{}; + stl::vector func{}; +}; + +struct meta_type_node { + using size_type = stl::size_t; + + const type_info *info{}; + id_type alias{}; + 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{}; + stl::unique_ptr details{}; +}; + +template +[[nodiscard]] auto *find_member(Type &from, const Value value) { + for(auto &&elem: from) { + if(elem.id == value) { + return &elem; + } + } + + return static_cast(nullptr); +} + +[[nodiscard]] inline auto *find_overload(meta_func_node *curr, stl::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 = stl::remove_reference_t*Member))>::value_type; + + if(node.details) { + if(auto *member = find_member((node.details.get()->*Member), id); member != nullptr) { + return member; + } + + if(recursive) { + for(auto &&curr: node.details->base) { + if(auto *elem = look_for(context, curr.type(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 stl::size_t index) noexcept { + using resolve_type = const meta_type_node &(*)(const meta_context &) noexcept; + constexpr stl::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.id == to) { + return other; + } else if(const void *elem = try_cast(context, curr.type(context), to, other); elem) { + return elem; + } + } + } + + return nullptr; +} + +template +auto setup_node_for() noexcept { + meta_type_node node{ + &type_id(), + type_id().hash(), + nullptr, + (stl::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) + | (stl::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) + | (stl::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) + | (stl::is_array_v ? meta_traits::is_array : meta_traits::is_none) + | (stl::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) + | (stl::is_class_v ? meta_traits::is_class : meta_traits::is_none) + | (stl::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(stl::is_default_constructible_v) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, stl::in_place_type}; + }; + } + + if constexpr(stl::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(stl::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(!stl::is_void_v && !stl::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, stl::in_place, static_cast *>(elem)}; + } + + if(elem) { // non-const reference construction request + return meta_any{ctx, stl::in_place_type &>, *static_cast *>(elem)}; + } + + // const reference construction request + return meta_any{ctx, stl::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 stl::size_t index) noexcept -> decltype(auto) { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; + } + + return node; +} + +template +[[nodiscard]] const meta_type_node &resolve(const meta_context &context) noexcept { + static const meta_type_node node = setup_node_for(); + const auto it = context.bucket.find(node.info->hash()); + return (it == context.bucket.cend()) ? node : *it->second; +} + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif diff --git a/include/entt/meta/pointer.hpp b/include/entt/meta/pointer.hpp new file mode 100644 index 0000000..e347756 --- /dev/null +++ b/include/entt/meta/pointer.hpp @@ -0,0 +1,42 @@ +// IWYU pragma: always_keep + +#ifndef ENTT_META_POINTER_HPP +#define ENTT_META_POINTER_HPP + +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "type_traits.hpp" + +namespace entt { + +/** + * @brief Makes `stl::shared_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + */ +template +struct is_meta_pointer_like> + : stl::true_type {}; + +/** + * @brief Makes `stl::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> + : stl::true_type {}; + +/** + * @brief Specialization for self-proclaimed meta pointer like types. + * @tparam Type Element type. + */ +template +requires requires { typename Type::is_meta_pointer_like; } +struct is_meta_pointer_like + : stl::true_type {}; + +} // namespace entt + +#endif diff --git a/include/entt/meta/policy.hpp b/include/entt/meta/policy.hpp new file mode 100644 index 0000000..be0e3d9 --- /dev/null +++ b/include/entt/meta/policy.hpp @@ -0,0 +1,81 @@ +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include "../stl/type_traits.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +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 ENTT_INTERNAL */ + 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 ENTT_INTERNAL */ + 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 ENTT_INTERNAL */ + template + static constexpr bool value = stl::is_reference_v && !stl::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 ENTT_INTERNAL */ + template + static constexpr bool value = stl::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 ENTT_INTERNAL */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/** + * @brief Provides the member constant `value` equal to true if a type also is a + * meta policy, false otherwise. + * @tparam Type Type to check. + */ +template +struct is_meta_policy + : stl::bool_constant> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template +inline constexpr bool is_meta_policy_v = is_meta_policy::value; + +/** + * @brief Specifies whether a type is a meta policy. + * @tparam Type Type to check. + */ +template +concept meta_policy = is_meta_policy_v; + +} // namespace entt + +#endif diff --git a/include/entt/meta/range.hpp b/include/entt/meta/range.hpp new file mode 100644 index 0000000..2e47d79 --- /dev/null +++ b/include/entt/meta/range.hpp @@ -0,0 +1,119 @@ +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/iterator.hpp" +#include "../stl/utility.hpp" +#include "context.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +struct meta_base_node; + +template +struct meta_range_iterator final { + using value_type = stl::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::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(stl::is_same_v) { + return {it[value].first, Type{*ctx, *it[value].second}}; + } 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); + } + + [[nodiscard]] constexpr stl::ptrdiff_t operator-(const meta_range_iterator &other) const noexcept { + return it - other.it; + } + + [[nodiscard]] constexpr bool operator==(const meta_range_iterator &other) const noexcept { + return it == other.it; + } + + [[nodiscard]] constexpr auto operator<=>(const meta_range_iterator &other) const noexcept { + return it <=> other.it; + } + +private: + It it; + const meta_ctx *ctx; +}; + +} // 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 diff --git a/include/entt/meta/resolve.hpp b/include/entt/meta/resolve.hpp new file mode 100644 index 0000000..7f3eb97 --- /dev/null +++ b/include/entt/meta/resolve.hpp @@ -0,0 +1,109 @@ +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include "../core/type_info.hpp" +#include "../locator/locator.hpp" +#include "../stl/type_traits.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.bucket.cbegin()}, {ctx, context.bucket.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 alias 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 alias) noexcept { + const auto &context = internal::meta_context::from(ctx); + + // fast lookup for unsearchable and overloaded types + if(const auto it = context.bucket.find(alias); it != context.bucket.end()) { + return meta_type{ctx, *it->second}; + } + + for(auto &&curr: context.bucket) { + if(curr.second->alias == alias) { + return meta_type{ctx, *curr.second}; + } + } + + return meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param alias Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const id_type alias) noexcept { + return resolve(locator::value_or(), alias); +} + +/** + * @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 it = context.bucket.find(info.hash()); + return (it == context.bucket.cend()) ? meta_type{} : meta_type{ctx, *it->second}; +} + +/** + * @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 diff --git a/include/entt/meta/template.hpp b/include/entt/meta/template.hpp new file mode 100644 index 0000000..5eab2af --- /dev/null +++ b/include/entt/meta/template.hpp @@ -0,0 +1,29 @@ +// 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 diff --git a/include/entt/meta/type_traits.hpp b/include/entt/meta/type_traits.hpp new file mode 100644 index 0000000..cc271fc --- /dev/null +++ b/include/entt/meta/type_traits.hpp @@ -0,0 +1,54 @@ +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" + +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` equal to true if a given type is + * a pointer-like type, false otherwise. + */ +template +struct is_meta_pointer_like: stl::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 diff --git a/include/entt/meta/utility.hpp b/include/entt/meta/utility.hpp new file mode 100644 index 0000000..ea5b01e --- /dev/null +++ b/include/entt/meta/utility.hpp @@ -0,0 +1,500 @@ +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include "../core/type_traits.hpp" +#include "../locator/locator.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.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, + stl::conditional_t, type_list, type_list>, + !stl::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, + stl::conditional_t, type_list, type_list>, + !stl::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 &, + stl::conditional_t, type_list<>, type_list>, + !stl::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, + stl::conditional_t< + stl::is_same_v, Type> || stl::is_base_of_v, Type>, + type_list, + type_list>, + !(stl::is_same_v, Type> || stl::is_base_of_v, Type>), + stl::is_const_v> && (stl::is_same_v, Type> || stl::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 meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...) const); + + template + static meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...)); + + template + requires stl::is_member_object_pointer_v + static meta_function_descriptor get_rid_of_noexcept(Ret Class::*); + + template + static meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); + + template + static 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(stl::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 = 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]] meta_any meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { + if constexpr(stl::is_same_v) { + static_assert(stl::is_lvalue_reference_v, "Invalid type"); + return meta_any{ctx, stl::in_place_type &>, stl::as_const(value)}; + } else if constexpr(stl::is_same_v || (stl::is_same_v && stl::is_lvalue_reference_v)) { + return meta_any{ctx, stl::in_place_type, value}; + } else if constexpr(stl::is_same_v) { + return meta_any{ctx, stl::in_place_type}; + } else { + return meta_any{ctx, stl::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]] meta_any meta_dispatch(Type &&value) { + return meta_dispatch(locator::value_or(), stl::forward(value)); +} + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(stl::is_void_v(candidate), args...))>) { + stl::invoke(stl::forward(candidate), args...); + return meta_any{ctx, stl::in_place_type}; + } else { + return meta_dispatch(ctx, stl::invoke(stl::forward(candidate), args...)); + } +} + +template +[[nodiscard]] meta_any meta_invoke(meta_any &instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args, stl::index_sequence) { + using descriptor = meta_function_helper_t>; + + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and stl::span) + if constexpr(stl::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(), stl::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else if constexpr(stl::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(), stl::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else { + if(((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(instance.context(), stl::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, stl::index_sequence) { + // NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and stl::span) + if(((args + Index)->allow_cast() && ...)) { + return meta_any{ctx, stl::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 stl::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 stl::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 args Parameters 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 *const args) { + if constexpr(stl::is_member_function_pointer_v || stl::is_function_v>>) { + return static_cast(internal::meta_invoke(*instance.operator->(), Data, args, stl::make_index_sequence::args_type::size>{})); + } else if constexpr(stl::is_member_object_pointer_v) { + using data_type = stl::remove_reference_t::return_type>; + + if constexpr(!stl::is_array_v && !stl::is_const_v) { + if(auto *const clazz = instance->try_cast(); clazz && args->allow_cast()) { + stl::invoke(Data, *clazz) = args->cast(); + return true; + } + } + + return false; + } else if constexpr(stl::is_pointer_v) { + using data_type = stl::remove_reference_t; + + if constexpr(!stl::is_array_v && !stl::is_const_v) { + if(args->allow_cast()) { + *Data = args->cast(); + return true; + } + } + + return false; + } else { + return false; + } +} + +/** + * @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(meta_handle instance, meta_any value) { + return meta_setter(*instance.operator->(), &value); +} + +/** + * @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. + * @param args Parameters to use to set the variable. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] meta_any meta_getter(meta_handle instance, [[maybe_unused]] meta_any *const args) { + if constexpr(stl::is_member_function_pointer_v || stl::is_function_v>>) { + return internal::meta_invoke(*instance.operator->(), Data, args, stl::make_index_sequence::args_type::size>{}); + } else if constexpr(stl::is_member_object_pointer_v) { + if constexpr(!stl::is_array_v>>) { + if(auto *clazz = instance->try_cast(); clazz) { + return meta_dispatch(instance->context(), stl::invoke(Data, *clazz)); + } else if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(instance->context(), stl::invoke(Data, *fallback)); + } + } + + return meta_any{meta_ctx_arg, instance->context()}; + } else if constexpr(stl::is_pointer_v) { + if constexpr(stl::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 Gets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] meta_any meta_getter(meta_handle instance) { + return meta_getter(*instance.operator->(), nullptr); +} + +/** + * @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]] meta_any meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke(*instance.operator->(), stl::forward(candidate), args, stl::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]] meta_any meta_invoke(meta_handle instance, meta_any *const args) { + return internal::meta_invoke(*instance.operator->(), Candidate, args, stl::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, stl::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 || stl::is_class_v>) { + meta_any placeholder{meta_ctx_arg, ctx}; + return internal::meta_invoke(placeholder, stl::forward(candidate), args, stl::make_index_sequence>::args_type::size>{}); + } else { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and stl::span) + return internal::meta_invoke(*args, stl::forward(candidate), args + 1u, stl::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]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct(locator::value_or(), stl::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]] 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]] meta_any meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +} // namespace entt + +#endif diff --git a/include/entt/natvis/config.natvis b/include/entt/natvis/config.natvis new file mode 100644 index 0000000..6eb47e3 --- /dev/null +++ b/include/entt/natvis/config.natvis @@ -0,0 +1,3 @@ + + + diff --git a/include/entt/natvis/container.natvis b/include/entt/natvis/container.natvis new file mode 100644 index 0000000..5bd4ac6 --- /dev/null +++ b/include/entt/natvis/container.natvis @@ -0,0 +1,39 @@ + + + + + + {{ size={ size() } }} + + packed.first_base::value.capacity() + bucket_count() + (float)size() / (float)bucket_count() + threshold + + size() + packed.first_base::value[$i].element + + + + + + + {{ size={ size() } }} + + packed.first_base::value.capacity() + bucket_count() + (float)size() / (float)bucket_count() + threshold + + size() + packed.first_base::value[$i].second + + + + + { payload } + + payload + + + diff --git a/include/entt/natvis/core.natvis b/include/entt/natvis/core.natvis new file mode 100644 index 0000000..1e9dd19 --- /dev/null +++ b/include/entt/natvis/core.natvis @@ -0,0 +1,32 @@ + + + + {{ policy={ mode,en } }} + + + + + + + ({ first() }, { second() }) + + first() + second() + + + + {{ hash={ base_type::hash } }} + {{}} + + base_type::repr,na + base_type::length + + + + {{ name={ alias,na } }} + + identifier + seq + + + diff --git a/include/entt/natvis/entity.natvis b/include/entt/natvis/entity.natvis new file mode 100644 index 0000000..f8fe42c --- /dev/null +++ b/include/entt/natvis/entity.natvis @@ -0,0 +1,181 @@ + + + + {{ pools={ pools.size() } }} + + entities + + { pools.size() } + + + + + + + + *pools.packed.first_base::value[pos].element.second,view(simple) + + ++pos + + + + + groups.size() + + { vars.ctx.size() } + + + + + + + + vars.ctx.packed.first_base::value[pos].element.second + + ++pos + + + + + + + + + + + + + + + {{ size={ packed.size() }, type={ descriptor->alias,na } }} + + packed.capacity() + mode,en + head + + { sparse.size() * traits_type::page_size } + + + + + + + + + page = pos / traits_type::page_size + offset = pos & (traits_type::page_size - 1) + + *((traits_type::entity_type *)&sparse[page][offset]) & traits_type::entity_mask + + ++pos + + + + + + { packed.size() } + + + + + + + + packed[pos] + + ++pos + + + + + + + + + + + {{ size={ base_type::packed.size() }, type={ base_type::descriptor->alias,na } }} + + payload.capacity() * traits_type::page_size + traits_type::page_size + placeholder + (base_type*)this,nand + (base_type*)this,view(simple)nand + + + + + + + payload[pos / traits_type::page_size][pos & (traits_type::page_size - 1)] + + ++pos + + + + + + {{ size_hint={ pools[index]->packed.size() } }} + {{ size_hint=0 }} + + pools,na + filter,na + pools[index],na + + + + {{ size={ leading->packed.size() } }} + {{ size=0 }} + + leading,na + + + + { *(base_type*)this } + + *(base_type*)this + + + + {{ size_hint={ pools[0]->packed.size() } }} + {{ size_hint=0 }} + + pools,na + filter,na + + + + + + + {{ entity={ entt } }} + + entt + owner,na + + + + + + + + + + + + pool_at(pos),view(simple)nanr + + ++pos + + + + + + + + <null> + + + <tombstone> + + diff --git a/include/entt/natvis/graph.natvis b/include/entt/natvis/graph.natvis new file mode 100644 index 0000000..d585ae2 --- /dev/null +++ b/include/entt/natvis/graph.natvis @@ -0,0 +1,19 @@ + + + + {{ size={ vert } }} + + + + + + + + pos % vert + + ++pos + + + + + diff --git a/include/entt/natvis/locator.natvis b/include/entt/natvis/locator.natvis new file mode 100644 index 0000000..6eb47e3 --- /dev/null +++ b/include/entt/natvis/locator.natvis @@ -0,0 +1,3 @@ + + + diff --git a/include/entt/natvis/meta.natvis b/include/entt/natvis/meta.natvis new file mode 100644 index 0000000..739897e --- /dev/null +++ b/include/entt/natvis/meta.natvis @@ -0,0 +1,200 @@ + + + + {{ id={ id } }} + {{}} + + id + + + + {{ id={ id } }} + {{}} + + id + + + + {{ id={ id } }} + {{}} + + id + arity + + + + {{ id={ id } }} + {{}} + + id + value + + + + + + + {{ id={ name,na } }} + {{ id={ id } }} + {{}} + + id + name,na + arity + has_trait(entt::internal::meta_traits::is_const) + has_trait(entt::internal::meta_traits::is_static) + custom + + + + + + + {{ id={ name,na } }} + {{ id={ id } }} + {{}} + + id + name,na + arity + has_trait(entt::internal::meta_traits::is_const) + has_trait(entt::internal::meta_traits::is_static) + *next + custom + + + + {{ arity={ arity } }} + {{}} + + arity + + + + + + ctor,view(simple) + base,view(simple) + conv,view(simple) + data,view(simple) + func,view(simple) + + + + + + + {{ type={ name,na } }} + {{ type={ info->alias,na } }} + {{}} + + alias + name,na + size_of + has_trait(entt::internal::meta_traits::is_arithmetic) + has_trait(entt::internal::meta_traits::is_integral) + has_trait(entt::internal::meta_traits::is_signed) + has_trait(entt::internal::meta_traits::is_array) + has_trait(entt::internal::meta_traits::is_enum) + has_trait(entt::internal::meta_traits::is_class) + has_trait(entt::internal::meta_traits::is_pointer) + has_trait(entt::internal::meta_traits::is_pointer_like) + has_trait(entt::internal::meta_traits::is_sequence_container) + has_trait(entt::internal::meta_traits::is_associative_container) + default_constructor != nullptr + conversion_helper != nullptr + from_void != nullptr + templ + custom + *details + + + + { storage } + {{}} + + node,na + ctx,na + + + + { any } + + any + + + + {{ const={ const_only } }} + {{}} + + ctx,na + const_only + data + + + + {{ const={ const_only } }} + {{}} + + ctx,na + const_only + data + + + + { node,na } + {{}} + + node + + + + { node,na } + {{}} + + node + ctx,na + + + + { node,na } + {{}} + + node + ctx,na + + + + { node,na } + {{}} + + node + ctx,na + + + + { node,na } + {{}} + + node + ctx,na + + + + + + + { bucket } + + + + + + + element_at(pos).second + ++pos + + + + + diff --git a/include/entt/natvis/poly.natvis b/include/entt/natvis/poly.natvis new file mode 100644 index 0000000..8dfd72a --- /dev/null +++ b/include/entt/natvis/poly.natvis @@ -0,0 +1,6 @@ + + + + { storage } + + diff --git a/include/entt/natvis/process.natvis b/include/entt/natvis/process.natvis new file mode 100644 index 0000000..1f959ff --- /dev/null +++ b/include/entt/natvis/process.natvis @@ -0,0 +1,21 @@ + + + + {{ state={ current,en } }} + + current,en + *next.first_base::value + + + + + {{ size={ size() } }} + + handlers.first_base::value.capacity() + + size() + *handlers.first_base::value[$i] + + + + diff --git a/include/entt/natvis/resource.natvis b/include/entt/natvis/resource.natvis new file mode 100644 index 0000000..cecd3b9 --- /dev/null +++ b/include/entt/natvis/resource.natvis @@ -0,0 +1,26 @@ + + + + { value } + + value + + + + + {{ size={ size() } }} + + + + + + + + *pool.first_base::value.packed.first_base::value[pos].element.second + + ++pos + + + + + diff --git a/include/entt/natvis/signal.natvis b/include/entt/natvis/signal.natvis new file mode 100644 index 0000000..b486810 --- /dev/null +++ b/include/entt/natvis/signal.natvis @@ -0,0 +1,51 @@ + + + + {{ type={ "$T1" } }} + + fn == nullptr + instance + + + + + {{ size={ size() } }} + + + size() + *pools.first_base::value.packed.first_base::value[$i].element.second + + + + + {{ size={ events.size() }, event={ "$T1" } }} + + signal + events,view(simple) + + + + {{ size={ handlers.first_base::value.size() } }} + + + {{ bound={ signal != nullptr } }} + + + { conn } + + + {{ size={ calls.size() }, type={ "$T1" } }} + + + calls.size() + calls[$i] + + + + + {{ type={ "$T1" } }} + + signal,na + + + diff --git a/include/entt/poly/fwd.hpp b/include/entt/poly/fwd.hpp new file mode 100644 index 0000000..4d513e3 --- /dev/null +++ b/include/entt/poly/fwd.hpp @@ -0,0 +1,21 @@ +#ifndef ENTT_POLY_FWD_HPP +#define ENTT_POLY_FWD_HPP + +#include "../stl/cstddef.hpp" + +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 diff --git a/include/entt/poly/poly.hpp b/include/entt/poly/poly.hpp new file mode 100644 index 0000000..a409c9a --- /dev/null +++ b/include/entt/poly/poly.hpp @@ -0,0 +1,316 @@ +#ifndef ENTT_POLY_POLY_HPP +#define ENTT_POLY_POLY_HPP + +#include "../core/any.hpp" +#include "../core/concepts.hpp" +#include "../core/type_info.hpp" +#include "../core/type_traits.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "fwd.hpp" + +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 = Concept::template type; + + template + requires stl::derived_from> + static auto vtable_entry(Ret (*)(Clazz &, Args...)) + -> Ret (*)(constness_as_t, Clazz> &, Args...); + + template + static auto vtable_entry(Ret (*)(Args...)) + -> Ret (*)(const basic_any &, Args...); + + template + requires stl::derived_from + static auto vtable_entry(Ret (Clazz::*)(Args...)) + -> Ret (*)(basic_any &, Args...); + + template + requires stl::derived_from + static auto vtable_entry(Ret (Clazz::*)(Args...) const) + -> Ret (*)(const basic_any &, Args...); + + template + static auto make_vtable(value_list) noexcept + -> decltype(stl::make_tuple(vtable_entry(Candidate)...)); + + template + [[nodiscard]] static ENTT_CONSTEVAL auto make_vtable(type_list) noexcept { + if constexpr(sizeof...(Func) == 0u) { + return decltype(make_vtable(typename Concept::template impl{})){}; + } else if constexpr((stl::is_function_v && ...)) { + return decltype(stl::make_tuple(vtable_entry(stl::declval())...)){}; + } + } + + template + static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) noexcept { + if constexpr(stl::is_invocable_r_v) { + entry = +[](Any &, Args... args) -> Ret { + return stl::invoke(Candidate, stl::forward(args)...); + }; + } else { + entry = +[](Any &instance, Args... args) -> Ret { + return static_cast(stl::invoke(Candidate, any_cast &>(instance), stl::forward(args)...)); + }; + } + } + + template + [[nodiscard]] static auto fill_vtable(stl::index_sequence) noexcept { + vtable_type impl{}; + (fill_vtable_entry>>(stl::get(impl)), ...); + return impl; + } + + using vtable_type = decltype(make_vtable(Concept{})); + static constexpr bool is_mono = stl::tuple_size_v == 1u; + +public: + /*! @brief Virtual table type. */ + using type = stl::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 const vtable_type vtable = fill_vtable(stl::make_index_sequence::size>{}); + + if constexpr(is_mono) { + return stl::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(stl::is_function_v>) { + return poly.vtable(poly.storage, stl::forward(args)...); + } else { + return stl::get(*poly.vtable)(poly.storage, stl::forward(args)...); + } + } + + /*! @copydoc invoke */ + template + [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) { + auto &poly = static_cast(self); + + if constexpr(stl::is_function_v>) { + static_assert(Member == 0u, "Unknown member"); + return poly.vtable(poly.storage, stl::forward(args)...); + } else { + return stl::get(*poly.vtable)(poly.storage, stl::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 stl::forward(self).template invoke(self, stl::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 = Concept::template type>; + /*! @brief Virtual table type. */ + using vtable_type = 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(stl::in_place_type_t, Args &&...args) + : storage{stl::in_place_type, stl::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 + requires (!stl::same_as, basic_poly>) + basic_poly(Type &&value) noexcept + : basic_poly{stl::in_place_type>, stl::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(); + } + + /** + * @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(stl::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 diff --git a/include/entt/process/fwd.hpp b/include/entt/process/fwd.hpp new file mode 100644 index 0000000..77bc495 --- /dev/null +++ b/include/entt/process/fwd.hpp @@ -0,0 +1,23 @@ +#ifndef ENTT_PROCESS_FWD_HPP +#define ENTT_PROCESS_FWD_HPP + +#include "../stl/cstdint.hpp" +#include "../stl/memory.hpp" + +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 diff --git a/include/entt/process/process.hpp b/include/entt/process/process.hpp new file mode 100644 index 0000000..8e5c950 --- /dev/null +++ b/include/entt/process/process.hpp @@ -0,0 +1,316 @@ +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include "../core/compressed_pair.hpp" +#include "../core/type_traits.hpp" +#include "../stl/cstdint.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +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 stl::enable_shared_from_this> { + enum class state : stl::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 = stl::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() = stl::allocate_shared(allocator, allocator, stl::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() = stl::allocate_shared(allocator, allocator, stl::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 ENTT_INTERNAL */ +namespace internal { + +template +struct process_adaptor: public basic_process { + using allocator_type = Allocator; + using base_type = basic_process; + using delta_type = base_type::delta_type; + + process_adaptor(const allocator_type &allocator, Func proc) + : base_type{allocator}, + func{stl::move(proc)} {} + + void update(const delta_type delta, void *data) override { + func(*this, delta, data); + } + +private: + Func func; +}; + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif diff --git a/include/entt/process/scheduler.hpp b/include/entt/process/scheduler.hpp new file mode 100644 index 0000000..554f7c2 --- /dev/null +++ b/include/entt/process/scheduler.hpp @@ -0,0 +1,227 @@ +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + +#include "../config/config.h" +#include "../core/compressed_pair.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "fwd.hpp" +#include "process.hpp" + +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 = stl::allocator_traits; + using container_allocator = alloc_traits::template rebind_alloc>; + using container_type = stl::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 = stl::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{stl::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{stl::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 stl::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(stl::allocate_shared(allocator, allocator, stl::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(stl::allocate_shared(allocator, allocator, stl::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 = stl::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 diff --git a/include/entt/resource/cache.hpp b/include/entt/resource/cache.hpp new file mode 100644 index 0000000..99c10e0 --- /dev/null +++ b/include/entt/resource/cache.hpp @@ -0,0 +1,386 @@ +#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP +#define ENTT_RESOURCE_RESOURCE_CACHE_HPP + +#include +#include "../container/dense_map.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/fwd.hpp" +#include "../core/iterator.hpp" +#include "../stl/concepts.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/iterator.hpp" +#include "../stl/memory.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "fwd.hpp" +#include "loader.hpp" +#include "resource.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +class resource_cache_iterator final { + template + friend class resource_cache_iterator; + +public: + using value_type = stl::pair>; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = stl::ptrdiff_t; + using iterator_category = stl::input_iterator_tag; + using iterator_concept = stl::random_access_iterator_tag; + + constexpr resource_cache_iterator() noexcept = default; + + constexpr resource_cache_iterator(const It iter) noexcept + : it{iter} {} + + template + requires (!stl::same_as && stl::constructible_from) + 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 + [[nodiscard]] constexpr stl::ptrdiff_t operator-(const resource_cache_iterator &other) const noexcept { + return it - other.it; + } + + template + [[nodiscard]] constexpr bool operator==(const resource_cache_iterator &other) const noexcept { + return it == other.it; + } + + template + [[nodiscard]] constexpr auto operator<=>(const resource_cache_iterator &other) const noexcept { + return it <=> other.it; + } + +private: + It it; +}; + +} // 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 = stl::allocator_traits; + static_assert(stl::is_same_v, "Invalid value type"); + using container_allocator = 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 = stl::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{stl::piecewise_construct, stl::forward_as_tuple(other.pool.first(), allocator), stl::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{stl::piecewise_construct, stl::forward_as_tuple(stl::move(other.pool.first()), allocator), stl::forward_as_tuple(stl::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 + stl::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()(stl::forward(args)...)); + } + + /** + * @brief Force loads a resource, even if its identifier already exists. + * @copydetails load + */ + template + stl::pair force_load(const id_type id, Args &&...args) { + return {pool.first().insert_or_assign(id, pool.second()(stl::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 undefined 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 diff --git a/include/entt/resource/fwd.hpp b/include/entt/resource/fwd.hpp new file mode 100644 index 0000000..bb9c542 --- /dev/null +++ b/include/entt/resource/fwd.hpp @@ -0,0 +1,19 @@ +#ifndef ENTT_RESOURCE_FWD_HPP +#define ENTT_RESOURCE_FWD_HPP + +#include "../stl/memory.hpp" + +namespace entt { + +template +struct resource_loader; + +template, typename = stl::allocator> +class resource_cache; + +template +class resource; + +} // namespace entt + +#endif diff --git a/include/entt/resource/loader.hpp b/include/entt/resource/loader.hpp new file mode 100644 index 0000000..2df95d0 --- /dev/null +++ b/include/entt/resource/loader.hpp @@ -0,0 +1,33 @@ +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + +#include "../stl/memory.hpp" +#include "../stl/utility.hpp" +#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 = stl::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 stl::make_shared(stl::forward(args)...); + } +}; + +} // namespace entt + +#endif diff --git a/include/entt/resource/resource.hpp b/include/entt/resource/resource.hpp new file mode 100644 index 0000000..670569d --- /dev/null +++ b/include/entt/resource/resource.hpp @@ -0,0 +1,212 @@ +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include +#include "../stl/concepts.hpp" +#include "../stl/memory.hpp" +#include "../stl/utility.hpp" +#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; + +public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = stl::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{stl::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, stl::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 + requires (!stl::same_as && stl::constructible_from) + 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 + requires (!stl::same_as && stl::constructible_from) + resource(resource &&other) noexcept + : value{stl::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 + requires (!stl::same_as && stl::constructible_from) + 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 + requires (!stl::same_as && stl::constructible_from) + resource &operator=(resource &&other) noexcept { + value = stl::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 stl::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 Compares two handles. + * @tparam Other Type of resource managed by the other handle. + * @param other A valid handle. + * @return True if both handles refer to the same resource, false otherwise. + */ + template + [[nodiscard]] bool operator==(const resource &other) const noexcept { + return (value == other.value); + } + + /** + * @brief Lexicographically compares two handles. + * @tparam Other Type of resource managed by the other handle. + * @param other A valid handle. + * @return The relative order between the two handles. + */ + template + [[nodiscard]] auto operator<=>(const resource &other) const noexcept { + return (value <=> other.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 = stl::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; +}; + +} // namespace entt + +#endif diff --git a/include/entt/signal/delegate.hpp b/include/entt/signal/delegate.hpp new file mode 100644 index 0000000..0ecedcd --- /dev/null +++ b/include/entt/signal/delegate.hpp @@ -0,0 +1,314 @@ +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include "../config/config.h" +#include "../core/type_traits.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/tuple.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "fwd.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +requires stl::is_member_object_pointer_v +auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(stl::declval()...)); + +template +[[nodiscard]] ENTT_CONSTEVAL auto index_sequence_for(Ret (*)(Args...)) { + return stl::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 = stl::remove_const_t; + using delegate_type = return_type(const void *, Args...); + + template + [[nodiscard]] auto wrap(stl::index_sequence) noexcept { + return [](const void *, Args... args) -> return_type { + [[maybe_unused]] const auto arguments = stl::forward_as_tuple(stl::forward(args)...); + [[maybe_unused]] constexpr auto offset = !stl::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast(stl::invoke(Candidate, stl::forward>>(stl::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, stl::index_sequence) noexcept { + return [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + [[maybe_unused]] const auto arguments = stl::forward_as_tuple(stl::forward(args)...); + [[maybe_unused]] constexpr auto offset = !stl::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast(stl::invoke(Candidate, *curr, stl::forward>>(stl::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, stl::index_sequence) noexcept { + return [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + [[maybe_unused]] const auto arguments = stl::forward_as_tuple(stl::forward(args)...); + [[maybe_unused]] constexpr auto offset = !stl::is_invocable_r_v>...> * (sizeof...(Args) - sizeof...(Index)); + return static_cast(stl::invoke(Candidate, curr, stl::forward>>(stl::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(stl::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(stl::is_invocable_r_v) { + fn = [](const void *, Args... args) -> return_type { + return Ret(stl::invoke(Candidate, stl::forward(args)...)); + }; + } else if constexpr(stl::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(stl::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(stl::invoke(Candidate, *curr, stl::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(stl::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> return_type { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(stl::invoke(Candidate, curr, stl::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, stl::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 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 diff --git a/include/entt/signal/dispatcher.hpp b/include/entt/signal/dispatcher.hpp new file mode 100644 index 0000000..3a3ccee --- /dev/null +++ b/include/entt/signal/dispatcher.hpp @@ -0,0 +1,391 @@ +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + +#include "../container/dense_map.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/concepts.hpp" +#include "../core/fwd.hpp" +#include "../core/type_info.hpp" +#include "../stl/cstddef.hpp" +#include "../stl/functional.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#include "fwd.hpp" +#include "sigh.hpp" + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +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 stl::size_t size() const noexcept = 0; +}; + +template +class dispatcher_handler final: public basic_dispatcher_handler { + using alloc_traits = stl::allocator_traits; + using signal_type = sigh; + using container_type = stl::vector>; + +public: + using allocator_type = Allocator; + + dispatcher_handler(const allocator_type &allocator) + : signal{allocator}, + events{allocator} {} + + void publish() override { + container_type other{}; + other.swap(events); + + for(auto &&elem: other) { + signal.publish(elem); + } + } + + 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(stl::is_aggregate_v && (sizeof...(Args) != 0u || !stl::is_default_constructible_v)) { + events.push_back(Type{stl::forward(args)...}); + } else { + events.emplace_back(stl::forward(args)...); + } + } + + [[nodiscard]] stl::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; + // stl::shared_ptr because of its type erased allocator which is useful here + using mapped_type = stl::shared_ptr; + + using alloc_traits = stl::allocator_traits; + using container_allocator = alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + + template + [[nodiscard]] handler_type &assure(const id_type id) { + auto &&ptr = pools.first()[id]; + + if(!ptr) { + const auto &allocator = get_allocator(); + ptr = stl::allocate_shared>(allocator, allocator); + } + + return static_cast &>(*ptr); + } + + template + [[nodiscard]] const handler_type *assure(const id_type id) const { + 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 = stl::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{stl::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{stl::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 stl::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(), 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(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(), stl::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(), stl::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(stl::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(stl::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 diff --git a/include/entt/signal/emitter.hpp b/include/entt/signal/emitter.hpp new file mode 100644 index 0000000..1ef4951 --- /dev/null +++ b/include/entt/signal/emitter.hpp @@ -0,0 +1,181 @@ +#ifndef ENTT_SIGNAL_EMITTER_HPP +#define ENTT_SIGNAL_EMITTER_HPP + +#include "../container/dense_map.hpp" +#include "../core/compressed_pair.hpp" +#include "../core/fwd.hpp" +#include "../core/type_info.hpp" +#include "../stl/functional.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/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 = stl::function; + + using alloc_traits = stl::allocator_traits; + using container_allocator = 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 = stl::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{stl::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{stl::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(stl::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 stl::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(stl::function func) { + handlers.first().insert_or_assign(type_id().hash(), [func = stl::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 diff --git a/include/entt/signal/fwd.hpp b/include/entt/signal/fwd.hpp new file mode 100644 index 0000000..4543622 --- /dev/null +++ b/include/entt/signal/fwd.hpp @@ -0,0 +1,46 @@ +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include "../stl/memory.hpp" + +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 diff --git a/include/entt/signal/sigh.hpp b/include/entt/signal/sigh.hpp new file mode 100644 index 0000000..fb05a36 --- /dev/null +++ b/include/entt/signal/sigh.hpp @@ -0,0 +1,573 @@ +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include "../stl/cstddef.hpp" +#include "../stl/memory.hpp" +#include "../stl/type_traits.hpp" +#include "../stl/utility.hpp" +#include "../stl/vector.hpp" +#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 = stl::allocator_traits; + using delegate_type = delegate; + using container_type = stl::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = stl::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{stl::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{stl::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 stl::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(stl::is_void_v || !stl::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(stl::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else if constexpr(stl::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 + * established 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 + * established 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{stl::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 = stl::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 = signal_type::delegate_type; + using difference_type = 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 = stl::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(stl::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(stl::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(stl::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 diff --git a/include/entt/stl/algorithm.hpp b/include/entt/stl/algorithm.hpp new file mode 100644 index 0000000..8fc29d9 --- /dev/null +++ b/include/entt/stl/algorithm.hpp @@ -0,0 +1,22 @@ +#ifndef ENTT_STL_ALGORITHM_HPP +#define ENTT_STL_ALGORITHM_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::all_of; +using std::any_of; +using std::find_if; +using std::none_of; +using std::sort; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/array.hpp b/include/entt/stl/array.hpp new file mode 100644 index 0000000..27137da --- /dev/null +++ b/include/entt/stl/array.hpp @@ -0,0 +1,19 @@ +#ifndef ENTT_STL_ARRAY_HPP +#define ENTT_STL_ARRAY_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::array; +using std::get; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/atomic.hpp b/include/entt/stl/atomic.hpp new file mode 100644 index 0000000..2e5b8d1 --- /dev/null +++ b/include/entt/stl/atomic.hpp @@ -0,0 +1,18 @@ +#ifndef ENTT_STL_ATOMIC_HPP +#define ENTT_STL_ATOMIC_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::atomic; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/bit.hpp b/include/entt/stl/bit.hpp new file mode 100644 index 0000000..3dca634 --- /dev/null +++ b/include/entt/stl/bit.hpp @@ -0,0 +1,20 @@ +#ifndef ENTT_STL_BIT_HPP +#define ENTT_STL_BIT_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::bit_ceil; +using std::has_single_bit; +using std::popcount; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/cmath.hpp b/include/entt/stl/cmath.hpp new file mode 100644 index 0000000..932ba0f --- /dev/null +++ b/include/entt/stl/cmath.hpp @@ -0,0 +1,18 @@ +#ifndef ENTT_STL_CMATH_HPP +#define ENTT_STL_CMATH_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::ceil; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/concepts.hpp b/include/entt/stl/concepts.hpp new file mode 100644 index 0000000..27d78a1 --- /dev/null +++ b/include/entt/stl/concepts.hpp @@ -0,0 +1,24 @@ +#ifndef ENTT_STL_CONCEPTS_HPP +#define ENTT_STL_CONCEPTS_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::constructible_from; +using std::default_initializable; +using std::derived_from; +using std::integral; +using std::invocable; +using std::same_as; +using std::unsigned_integral; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/cstddef.hpp b/include/entt/stl/cstddef.hpp new file mode 100644 index 0000000..512d233 --- /dev/null +++ b/include/entt/stl/cstddef.hpp @@ -0,0 +1,21 @@ +#ifndef ENTT_STL_CSTDDEF_HPP +#define ENTT_STL_CSTDDEF_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::byte; +using std::nullptr_t; +using std::ptrdiff_t; +using std::size_t; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/cstdint.hpp b/include/entt/stl/cstdint.hpp new file mode 100644 index 0000000..ff6dfcb --- /dev/null +++ b/include/entt/stl/cstdint.hpp @@ -0,0 +1,21 @@ +#ifndef ENTT_STL_CSTDINT_HPP +#define ENTT_STL_CSTDINT_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::uint16_t; +using std::uint32_t; +using std::uint64_t; +using std::uint8_t; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/functional.hpp b/include/entt/stl/functional.hpp new file mode 100644 index 0000000..0b6b50b --- /dev/null +++ b/include/entt/stl/functional.hpp @@ -0,0 +1,55 @@ +#ifndef ENTT_STL_FUNCTIONAL_HPP +#define ENTT_STL_FUNCTIONAL_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include +# include +# include "../config/config.h" + +namespace entt::stl { + +using std::equal_to; +using std::function; +using std::hash; +using std::invoke; +using std::less; + +} // namespace entt::stl + +# ifndef ENTT_FORCE_STL +# if defined(__cpp_lib_ranges) +# define ENTT_HAS_IDENTITY +namespace entt::stl { + +using std::identity; + +} // namespace entt::stl +# endif +# endif + +# ifndef ENTT_HAS_IDENTITY +# include + +namespace entt::stl { + +struct identity { + using is_transparent = void; + + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +} // namespace entt::stl +# endif + +#endif +/*! @endcond */ + +#undef ENTT_HAS_IDENTITY + +#endif diff --git a/include/entt/stl/ios.hpp b/include/entt/stl/ios.hpp new file mode 100644 index 0000000..e1b692e --- /dev/null +++ b/include/entt/stl/ios.hpp @@ -0,0 +1,18 @@ +#ifndef ENTT_STL_IOS_HPP +#define ENTT_STL_IOS_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::boolalpha; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/iterator.hpp b/include/entt/stl/iterator.hpp new file mode 100644 index 0000000..1723e03 --- /dev/null +++ b/include/entt/stl/iterator.hpp @@ -0,0 +1,110 @@ +#ifndef ENTT_STL_ITERATOR_HPP +#define ENTT_STL_ITERATOR_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include +# include +# include "../config/config.h" + +namespace entt::stl { + +using std::advance; +using std::bidirectional_iterator_tag; +using std::distance; +using std::forward_iterator_tag; +using std::input_iterator_tag; +using std::iterator_traits; +using std::make_reverse_iterator; +using std::random_access_iterator_tag; +using std::reverse_iterator; + +# ifndef ENTT_FORCE_STL +# if defined(__cpp_lib_ranges) +# define ENTT_HAS_ITERATOR_CONCEPTS + +using std::bidirectional_iterator; +using std::forward_iterator; +using std::input_iterator; +using std::input_or_output_iterator; +using std::output_iterator; +using std::random_access_iterator; +using std::sentinel_for; + +# endif +# endif + +# ifndef ENTT_HAS_ITERATOR_CONCEPTS +# include +# include + +namespace internal { + +template +concept has_iterator_category = requires { + typename std::iterator_traits::iterator_category; +}; + +template +concept has_iterator_concept = has_iterator_category && requires { + typename It::iterator_concept; +}; + +template +struct iterator_tag { + using type = typename std::iterator_traits::iterator_category; +}; + +template +struct iterator_tag { + using type = typename It::iterator_concept; +}; + +template +concept has_iterator_tag = std::derived_from::type, Tag>; + +} // namespace internal + +// Bare minimum definitions to support broken platforms like PS4. +// EnTT does not provide full featured definitions for iterator concepts. + +template +concept input_or_output_iterator = requires(It it) { + *it; + { ++it } -> std::same_as; + it++; +}; + +template +concept input_iterator = input_or_output_iterator && internal::has_iterator_tag; + +template +concept output_iterator = input_or_output_iterator && requires(It it, Type &&value) { + *it++ = std::forward(value); +}; + +template +concept forward_iterator = input_iterator && internal::has_iterator_tag; + +template +concept bidirectional_iterator = forward_iterator && internal::has_iterator_tag; + +template +concept random_access_iterator = bidirectional_iterator && internal::has_iterator_tag; + +template +concept sentinel_for = input_or_output_iterator && requires(Sentinel sentinel, It it) { + { it == sentinel } -> std::same_as; +}; + +# endif + +} // namespace entt::stl +#endif +/*! @endcond */ + +#undef ENTT_HAS_ITERATOR_CONCEPTS + +#endif diff --git a/include/entt/stl/limits.hpp b/include/entt/stl/limits.hpp new file mode 100644 index 0000000..fca169b --- /dev/null +++ b/include/entt/stl/limits.hpp @@ -0,0 +1,18 @@ +#ifndef ENTT_STL_LIMITS_HPP +#define ENTT_STL_LIMITS_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::numeric_limits; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/memory.hpp b/include/entt/stl/memory.hpp new file mode 100644 index 0000000..e75fcab --- /dev/null +++ b/include/entt/stl/memory.hpp @@ -0,0 +1,74 @@ +#ifndef ENTT_STL_MEMORY_HPP +#define ENTT_STL_MEMORY_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include +# include +# include "../config/config.h" + +namespace entt::stl { + +using std::addressof; +using std::allocate_shared; +using std::allocator; +using std::allocator_arg; +using std::allocator_arg_t; +using std::allocator_traits; +using std::default_delete; +using std::destroy; +using std::enable_shared_from_this; +using std::make_shared; +using std::make_unique; +using std::pointer_traits; +using std::shared_ptr; +using std::static_pointer_cast; +using std::uninitialized_fill; +using std::unique_ptr; +using std::uses_allocator_v; + +} // namespace entt::stl + +# ifndef ENTT_FORCE_STL +# if defined(__cpp_lib_to_address) +# define ENTT_HAS_TO_ADDRESS + +namespace entt::stl { + +using std::to_address; + +} // namespace entt::stl + +# endif +# endif + +# ifndef ENTT_HAS_TO_ADDRESS +# include + +namespace entt::stl { + +template +constexpr Type *to_address(Type *ptr) noexcept { + static_assert(!std::is_function_v, "Invalid type"); + return ptr; +} + +template +constexpr auto to_address(const Type &ptr) noexcept { + if constexpr(requires { std::pointer_traits::to_address(ptr); }) { + return std::pointer_traits::to_address(ptr); + } else { + return to_address(ptr.operator->()); + } +} + +} // namespace entt::stl +# endif +#endif +/*! @endcond */ + +#undef ENTT_HAS_TO_ADDRESS + +#endif diff --git a/include/entt/stl/ostream.hpp b/include/entt/stl/ostream.hpp new file mode 100644 index 0000000..f185c9e --- /dev/null +++ b/include/entt/stl/ostream.hpp @@ -0,0 +1,18 @@ +#ifndef ENTT_STL_OSTREAM_HPP +#define ENTT_STL_OSTREAM_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::ostream; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/sstream.hpp b/include/entt/stl/sstream.hpp new file mode 100644 index 0000000..0c5c4b4 --- /dev/null +++ b/include/entt/stl/sstream.hpp @@ -0,0 +1,18 @@ +#ifndef ENTT_STL_SSTREAM_HPP +#define ENTT_STL_SSTREAM_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::stringstream; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/string.hpp b/include/entt/stl/string.hpp new file mode 100644 index 0000000..a0cc88f --- /dev/null +++ b/include/entt/stl/string.hpp @@ -0,0 +1,18 @@ +#ifndef ENTT_STL_STRING_HPP +#define ENTT_STL_STRING_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::string; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/string_view.hpp b/include/entt/stl/string_view.hpp new file mode 100644 index 0000000..bfe8956 --- /dev/null +++ b/include/entt/stl/string_view.hpp @@ -0,0 +1,19 @@ +#ifndef ENTT_STL_STRING_VIEW_HPP +#define ENTT_STL_STRING_VIEW_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::string_view; +using std::swap; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/tuple.hpp b/include/entt/stl/tuple.hpp new file mode 100644 index 0000000..fa085cd --- /dev/null +++ b/include/entt/stl/tuple.hpp @@ -0,0 +1,28 @@ +#ifndef ENTT_STL_TUPLE_HPP +#define ENTT_STL_TUPLE_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::apply; +using std::forward_as_tuple; +using std::get; +using std::make_from_tuple; +using std::make_tuple; +using std::tuple; +using std::tuple_cat; +using std::tuple_element; +using std::tuple_element_t; +using std::tuple_size; +using std::tuple_size_v; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/type_traits.hpp b/include/entt/stl/type_traits.hpp new file mode 100644 index 0000000..882f627 --- /dev/null +++ b/include/entt/stl/type_traits.hpp @@ -0,0 +1,70 @@ +#ifndef ENTT_STL_TYPE_TRAITS_HPP +#define ENTT_STL_TYPE_TRAITS_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::bool_constant; +using std::common_type_t; +using std::conditional_t; +using std::decay_t; +using std::extent_v; +using std::false_type; +using std::integral_constant; +using std::invoke_result_t; +using std::is_aggregate_v; +using std::is_arithmetic_v; +using std::is_array_v; +using std::is_base_of_v; +using std::is_class_v; +using std::is_const_v; +using std::is_constructible_v; +using std::is_convertible_v; +using std::is_copy_assignable_v; +using std::is_copy_constructible_v; +using std::is_default_constructible_v; +using std::is_empty_v; +using std::is_enum_v; +using std::is_final_v; +using std::is_function_v; +using std::is_integral_v; +using std::is_invocable; +using std::is_invocable_r; +using std::is_invocable_r_v; +using std::is_invocable_v; +using std::is_lvalue_reference_v; +using std::is_member_function_pointer_v; +using std::is_member_object_pointer_v; +using std::is_member_pointer_v; +using std::is_move_assignable_v; +using std::is_move_constructible_v; +using std::is_nothrow_constructible_v; +using std::is_nothrow_copy_constructible_v; +using std::is_nothrow_default_constructible_v; +using std::is_nothrow_destructible_v; +using std::is_nothrow_invocable_v; +using std::is_nothrow_move_constructible_v; +using std::is_pointer_v; +using std::is_reference_v; +using std::is_same_v; +using std::is_signed_v; +using std::is_trivially_destructible_v; +using std::is_void_v; +using std::remove_const_t; +using std::remove_cvref_t; +using std::remove_pointer_t; +using std::remove_reference_t; +using std::true_type; +using std::type_identity; +using std::underlying_type_t; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/utility.hpp b/include/entt/stl/utility.hpp new file mode 100644 index 0000000..2450d1b --- /dev/null +++ b/include/entt/stl/utility.hpp @@ -0,0 +1,34 @@ +#ifndef ENTT_STL_UTILITY_HPP +#define ENTT_STL_UTILITY_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::as_const; +using std::declval; +using std::exchange; +using std::forward; +using std::in_place; +using std::in_place_t; +using std::in_place_type; +using std::in_place_type_t; +using std::index_sequence; +using std::index_sequence_for; +using std::make_index_sequence; +using std::make_pair; +using std::move; +using std::pair; +using std::piecewise_construct; +using std::piecewise_construct_t; +using std::swap; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/stl/vector.hpp b/include/entt/stl/vector.hpp new file mode 100644 index 0000000..f5200d3 --- /dev/null +++ b/include/entt/stl/vector.hpp @@ -0,0 +1,18 @@ +#ifndef ENTT_STL_VECTOR_HPP +#define ENTT_STL_VECTOR_HPP + +/*! @cond ENTT_INTERNAL */ +#if __has_include() +# include +#else +# include + +namespace entt::stl { + +using std::vector; + +} // namespace entt::stl +#endif +/*! @endcond */ + +#endif diff --git a/include/entt/tools.hpp b/include/entt/tools.hpp new file mode 100644 index 0000000..66e9813 --- /dev/null +++ b/include/entt/tools.hpp @@ -0,0 +1,3 @@ +// IWYU pragma: begin_exports +#include "tools/davey.hpp" +// IWYU pragma: end_exports diff --git a/include/entt/tools/davey.hpp b/include/entt/tools/davey.hpp new file mode 100644 index 0000000..c305613 --- /dev/null +++ b/include/entt/tools/davey.hpp @@ -0,0 +1,343 @@ +#ifndef ENTT_TOOLS_DAVEY_HPP +#define ENTT_TOOLS_DAVEY_HPP + +#include "../config/config.h" +#include "../entity/mixin.hpp" +#include "../entity/registry.hpp" +#include "../entity/sparse_set.hpp" +#include "../entity/storage.hpp" +#include "../locator/locator.hpp" +#include "../meta/container.hpp" +#include "../meta/context.hpp" +#include "../meta/meta.hpp" +#include "../meta/pointer.hpp" +#include "../meta/resolve.hpp" +#include "../stl/cstdint.hpp" +#include "../stl/ios.hpp" +#include "../stl/sstream.hpp" +#include "../stl/string.hpp" + +#if __has_include() +# include +#endif + +namespace entt { + +/*! @cond ENTT_INTERNAL */ +namespace internal { + +template +static void present_element(const meta_any &obj, OnEntity on_entity) { + for([[maybe_unused]] const auto [id, data]: obj.type().data()) { + const auto elem = data.get(obj); + const stl::string name = data.name().empty() ? stl::string{data.type().info().name()} : stl::string{data.name()}; + const char *const label = name.c_str(); + + if(auto type = data.type(); type.info() == type_id()) { + ImGui::Text("%s: %s", label, elem.template cast()); + } else if(type.info() == type_id()) { + ImGui::Text("%s: %s", label, elem.template cast().data()); + } else if(type.info() == type_id()) { + if(const auto entt = elem.template cast(); entt == null) { + ImGui::Text("%s: %s", label, "null"); + } else { + on_entity(label, entt); + } + } else if(type.is_enum()) { + const char *as_string = nullptr; + + for(auto [id, curr]: type.data()) { + if(curr.get({}) == elem) { + as_string = curr.name().data(); + break; + } + } + + if(as_string) { + ImGui::Text("%s: %s", label, as_string); + } else { + ImGui::Text("%s: %zu", label, elem.template allow_cast().template cast()); + } + } else if(type.is_arithmetic()) { + if(type.info() == type_id()) { + stl::stringstream buffer{}; + buffer << stl::boolalpha << elem.template cast(); + ImGui::Text("%s: %s", label, buffer.str().data()); + } else if(type.info() == type_id()) { + ImGui::Text("%s: %c", label, elem.template cast()); + } else if(type.is_integral()) { + ImGui::Text("%s: %zu", label, elem.template allow_cast().template cast()); + } else { + ImGui::Text("%s: %f", label, elem.template allow_cast().template cast()); + } + } else if(type.is_pointer_like()) { + if(auto deref = *obj; deref) { + if(ImGui::TreeNode(label)) { + present_element(*obj, on_entity); + ImGui::TreePop(); + } + } else { + ImGui::Text("%s: %s", label, "null"); + } + } else if(type.is_sequence_container()) { + if(ImGui::TreeNode(label)) { + meta_sequence_container view = elem.as_sequence_container(); + + for(stl::size_t pos{}, last = view.size(); pos < last; ++pos) { + ImGui::PushID(static_cast(pos)); + + if(ImGui::TreeNode(label, "%zu", pos)) { + present_element(view[pos], on_entity); + ImGui::TreePop(); + } + + ImGui::PopID(); + } + + ImGui::TreePop(); + } + } else if(type.is_associative_container()) { + if(ImGui::TreeNode(label)) { + meta_associative_container view = elem.as_associative_container(); + auto it = view.begin(); + + for(stl::size_t pos{}, last = view.size(); pos < last; ++pos, ++it) { + ImGui::PushID(static_cast(pos)); + + if(ImGui::TreeNode(label, "%zu", pos)) { + const auto [key, value] = *it; + + if(ImGui::TreeNode("key")) { + present_element(key, on_entity); + ImGui::TreePop(); + } + + if(ImGui::TreeNode("value")) { + present_element(value, on_entity); + ImGui::TreePop(); + } + + ImGui::TreePop(); + } + + ImGui::PopID(); + } + + ImGui::TreePop(); + } + } else if(type.is_class()) { + if(ImGui::TreeNode(label)) { + present_element(elem, on_entity); + ImGui::TreePop(); + } + } else { + const stl::string underlying_type{data.type().info().name()}; + ImGui::Text("%s: %s", label, underlying_type.data()); + } + } + + for([[maybe_unused]] const auto [id, base]: obj.type().base()) { + present_element(obj.allow_cast(base.type()), on_entity); + } +} + +template +static void present_storage(const meta_ctx &ctx, const basic_sparse_set &storage) { + if(auto type = resolve(ctx, storage.info()); type) { + for(auto entt: storage) { + ImGui::PushID(static_cast(to_entity(entt))); + + if(ImGui::TreeNode(&storage.info(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) { + if(const auto obj = type.from_void(storage.value(entt)); obj) { + present_element::entity_type>(obj, [](const char *name, const Entity entt) { + ImGui::Text("%s: %d [%d/%d]", name, to_integral(entt), to_entity(entt), to_version(entt)); + }); + } + + ImGui::TreePop(); + } + + ImGui::PopID(); + } + } else { + for(auto entt: storage) { + ImGui::Text("%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt)); + } + } +} + +template +static void present_entity(const meta_ctx &ctx, const Entity entt, const It from, const It to) { + for(auto it = from; it != to; ++it) { + if(const auto &storage = it->second; storage.contains(entt)) { + if(auto type = resolve(ctx, storage.info()); type) { + const stl::string name = type.name().empty() ? stl::string{storage.info().name()} : stl::string{type.name()}; + const char *const label = name.c_str(); + + if(ImGui::TreeNode(&storage.info(), "%s", label)) { + if(const auto obj = type.from_void(storage.value(entt)); obj) { + present_element(obj, [&ctx, from, to](const char *name, const Entity other) { + if(ImGui::TreeNode(name, "%s: %d [%d/%d]", name, to_integral(other), to_entity(other), to_version(other))) { + present_entity(ctx, other, from, to); + ImGui::TreePop(); + } + }); + } + + ImGui::TreePop(); + } + } else { + const stl::string name{storage.info().name()}; + ImGui::Text("%s", name.data()); + } + } + } +} + +template +static void present_view(const meta_ctx &ctx, const basic_view, exclude_t> &view, stl::index_sequence) { + using view_type = basic_view, exclude_t>; + const stl::array range{view.template storage()...}; + + for(auto tup: view.each()) { + const auto entt = stl::get<0>(tup); + ImGui::PushID(static_cast(to_entity(entt))); + + if(ImGui::TreeNode(&type_id(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) { + for(const auto *storage: range) { + if(auto type = resolve(ctx, storage->info()); type) { + const stl::string name = type.name().empty() ? stl::string{storage->info().name()} : stl::string{type.name()}; + const char *const label = name.c_str(); + + if(ImGui::TreeNode(&storage->info(), "%s", label)) { + if(const auto obj = type.from_void(storage->value(entt)); obj) { + present_element(obj, [](const char *name, const view_type::entity_type entt) { + ImGui::Text("%s: %d [%d/%d]", name, to_integral(entt), to_entity(entt), to_version(entt)); + }); + } + + ImGui::TreePop(); + } + } else { + const stl::string name{storage->info().name()}; + ImGui::Text("%s", name.data()); + } + } + + ImGui::TreePop(); + } + + ImGui::PopID(); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief ImGui-based introspection tool for storage types. + * @tparam Type Storage element type. + * @tparam Entity Storage entity type. + * @tparam Allocator Storage allocator type. + * @param ctx The context from which to search for meta types. + * @param storage An instance of the storage type. + */ +template +void davey(const meta_ctx &ctx, const basic_storage &storage) { + internal::present_storage(ctx, storage); +} + +/** + * @brief ImGui-based introspection tool for storage types. + * @tparam Type Storage element type. + * @tparam Entity Storage entity type. + * @tparam Allocator Storage allocator type. + * @param storage An instance of the storage type. + */ +template +void davey(const basic_storage &storage) { + davey(locator::value_or(), storage); +} + +/** + * @brief ImGui-based introspection tool for view types. + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + * @param ctx The context from which to search for meta types. + * @param view An instance of the view type. + */ +template +void davey(const meta_ctx &ctx, const basic_view, exclude_t> &view) { + internal::present_view(ctx, view, stl::index_sequence_for{}); +} + +/** + * @brief ImGui-based introspection tool for view types. + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + * @param view An instance of the view type. + */ +template +void davey(const basic_view, exclude_t> &view) { + davey(locator::value_or(), view); +} + +/** + * @brief ImGui-based introspection tool for registry types. + * @tparam Entity Registry entity type. + * @tparam Allocator Registry allocator type. + * @param ctx The context from which to search for meta types. + * @param registry An instance of the registry type. + */ +template +void davey(const meta_ctx &ctx, const basic_registry ®istry) { + ImGui::BeginTabBar("#tabs"); + + if(ImGui::BeginTabItem("Entity")) { + for(const auto [entt]: registry.template storage()->each()) { + ImGui::PushID(static_cast(to_entity(entt))); + + if(ImGui::TreeNode(&type_id(), "%d [%d/%d]", to_integral(entt), to_entity(entt), to_version(entt))) { + const auto range = registry.storage(); + internal::present_entity(ctx, entt, range.begin(), range.end()); + ImGui::TreePop(); + } + + ImGui::PopID(); + } + + ImGui::EndTabItem(); + } + + if(ImGui::BeginTabItem("Storage")) { + for([[maybe_unused]] auto [id, storage]: registry.storage()) { + const auto type = resolve(ctx, storage.info()); + const stl::string name = type.name().empty() ? stl::string{storage.info().name()} : stl::string{type.name()}; + const char *const label = name.c_str(); + + if(ImGui::TreeNode(&storage.info(), "%s (%zu)", label, storage.size())) { + internal::present_storage(ctx, storage); + ImGui::TreePop(); + } + } + + ImGui::EndTabItem(); + } + + ImGui::EndTabBar(); +} + +/** + * @brief ImGui-based introspection tool for registry types. + * @tparam Entity Registry entity type. + * @tparam Allocator Registry allocator type. + * @param registry An instance of the registry type. + */ +template +void davey(const basic_registry ®istry) { + davey(locator::value_or(), registry); +} + +} // namespace entt + +#endif diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp deleted file mode 100644 index 82d69f7..0000000 --- a/include/nlohmann/json.hpp +++ /dev/null @@ -1,25526 +0,0 @@ -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - -/****************************************************************************\ - * Note on documentation: The source files contain links to the online * - * documentation of the public API at https://json.nlohmann.me. This URL * - * contains the most recent documentation and should also be applicable to * - * previous versions; documentation for deprecated functions is not * - * removed, but marked deprecated. See "Generate documentation" section in * - * file docs/README.md. * -\****************************************************************************/ - -#ifndef INCLUDE_NLOHMANN_JSON_HPP_ -#define INCLUDE_NLOHMANN_JSON_HPP_ - -#include // all_of, find, for_each -#include // nullptr_t, ptrdiff_t, size_t -#include // hash, less -#include // initializer_list -#ifndef JSON_NO_IO - #include // istream, ostream -#endif // JSON_NO_IO -#include // random_access_iterator_tag -#include // unique_ptr -#include // string, stoi, to_string -#include // declval, forward, move, pair, swap -#include // vector - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// This file contains all macro definitions affecting or depending on the ABI - -#ifndef JSON_SKIP_LIBRARY_VERSION_CHECK - #if defined(NLOHMANN_JSON_VERSION_MAJOR) && defined(NLOHMANN_JSON_VERSION_MINOR) && defined(NLOHMANN_JSON_VERSION_PATCH) - #if NLOHMANN_JSON_VERSION_MAJOR != 3 || NLOHMANN_JSON_VERSION_MINOR != 12 || NLOHMANN_JSON_VERSION_PATCH != 0 - #warning "Already included a different version of the library!" - #endif - #endif -#endif - -#define NLOHMANN_JSON_VERSION_MAJOR 3 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_MINOR 12 // NOLINT(modernize-macro-to-enum) -#define NLOHMANN_JSON_VERSION_PATCH 0 // NOLINT(modernize-macro-to-enum) - -#ifndef JSON_DIAGNOSTICS - #define JSON_DIAGNOSTICS 0 -#endif - -#ifndef JSON_DIAGNOSTIC_POSITIONS - #define JSON_DIAGNOSTIC_POSITIONS 0 -#endif - -#ifndef JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 0 -#endif - -#if JSON_DIAGNOSTICS - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS _diag -#else - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS -#endif - -#if JSON_DIAGNOSTIC_POSITIONS - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS _dp -#else - #define NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS -#endif - -#if JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON _ldvcmp -#else - #define NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_NO_VERSION - #define NLOHMANN_JSON_NAMESPACE_NO_VERSION 0 -#endif - -// Construct the namespace ABI tags component -#define NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) json_abi ## a ## b ## c -#define NLOHMANN_JSON_ABI_TAGS_CONCAT(a, b, c) \ - NLOHMANN_JSON_ABI_TAGS_CONCAT_EX(a, b, c) - -#define NLOHMANN_JSON_ABI_TAGS \ - NLOHMANN_JSON_ABI_TAGS_CONCAT( \ - NLOHMANN_JSON_ABI_TAG_DIAGNOSTICS, \ - NLOHMANN_JSON_ABI_TAG_LEGACY_DISCARDED_VALUE_COMPARISON, \ - NLOHMANN_JSON_ABI_TAG_DIAGNOSTIC_POSITIONS) - -// Construct the namespace version component -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) \ - _v ## major ## _ ## minor ## _ ## patch -#define NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(major, minor, patch) \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT_EX(major, minor, patch) - -#if NLOHMANN_JSON_NAMESPACE_NO_VERSION -#define NLOHMANN_JSON_NAMESPACE_VERSION -#else -#define NLOHMANN_JSON_NAMESPACE_VERSION \ - NLOHMANN_JSON_NAMESPACE_VERSION_CONCAT(NLOHMANN_JSON_VERSION_MAJOR, \ - NLOHMANN_JSON_VERSION_MINOR, \ - NLOHMANN_JSON_VERSION_PATCH) -#endif - -// Combine namespace components -#define NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) a ## b -#define NLOHMANN_JSON_NAMESPACE_CONCAT(a, b) \ - NLOHMANN_JSON_NAMESPACE_CONCAT_EX(a, b) - -#ifndef NLOHMANN_JSON_NAMESPACE -#define NLOHMANN_JSON_NAMESPACE \ - nlohmann::NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_BEGIN -#define NLOHMANN_JSON_NAMESPACE_BEGIN \ - namespace nlohmann \ - { \ - inline namespace NLOHMANN_JSON_NAMESPACE_CONCAT( \ - NLOHMANN_JSON_ABI_TAGS, \ - NLOHMANN_JSON_NAMESPACE_VERSION) \ - { -#endif - -#ifndef NLOHMANN_JSON_NAMESPACE_END -#define NLOHMANN_JSON_NAMESPACE_END \ - } /* namespace (inline namespace) NOLINT(readability/namespace) */ \ - } // namespace nlohmann -#endif - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // transform -#include // array -#include // forward_list -#include // inserter, front_inserter, end -#include // map -#ifdef JSON_HAS_CPP_17 - #include // optional -#endif -#include // string -#include // tuple, make_tuple -#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible -#include // unordered_map -#include // pair, declval -#include // valarray - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // nullptr_t -#include // exception -#if JSON_DIAGNOSTICS - #include // accumulate -#endif -#include // runtime_error -#include // to_string -#include // vector - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // array -#include // size_t -#include // uint8_t -#include // string - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // declval, pair -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template struct make_void -{ - using type = void; -}; -template using void_t = typename make_void::type; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -// https://en.cppreference.com/w/cpp/experimental/is_detected -struct nonesuch -{ - nonesuch() = delete; - ~nonesuch() = delete; - nonesuch(nonesuch const&) = delete; - nonesuch(nonesuch const&&) = delete; - void operator=(nonesuch const&) = delete; - void operator=(nonesuch&&) = delete; -}; - -template class Op, - class... Args> -struct detector -{ - using value_t = std::false_type; - using type = Default; -}; - -template class Op, class... Args> -struct detector>, Op, Args...> -{ - using value_t = std::true_type; - using type = Op; -}; - -template class Op, class... Args> -using is_detected = typename detector::value_t; - -template class Op, class... Args> -struct is_detected_lazy : is_detected { }; - -template class Op, class... Args> -using detected_t = typename detector::type; - -template class Op, class... Args> -using detected_or = detector; - -template class Op, class... Args> -using detected_or_t = typename detected_or::type; - -template class Op, class... Args> -using is_detected_exact = std::is_same>; - -template class Op, class... Args> -using is_detected_convertible = - std::is_convertible, To>; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - - -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-FileCopyrightText: 2016 - 2021 Evan Nemerson -// SPDX-License-Identifier: MIT - -/* Hedley - https://nemequ.github.io/hedley - * Created by Evan Nemerson - */ - -#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) -#if defined(JSON_HEDLEY_VERSION) - #undef JSON_HEDLEY_VERSION -#endif -#define JSON_HEDLEY_VERSION 15 - -#if defined(JSON_HEDLEY_STRINGIFY_EX) - #undef JSON_HEDLEY_STRINGIFY_EX -#endif -#define JSON_HEDLEY_STRINGIFY_EX(x) #x - -#if defined(JSON_HEDLEY_STRINGIFY) - #undef JSON_HEDLEY_STRINGIFY -#endif -#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) - -#if defined(JSON_HEDLEY_CONCAT_EX) - #undef JSON_HEDLEY_CONCAT_EX -#endif -#define JSON_HEDLEY_CONCAT_EX(a,b) a##b - -#if defined(JSON_HEDLEY_CONCAT) - #undef JSON_HEDLEY_CONCAT -#endif -#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) - -#if defined(JSON_HEDLEY_CONCAT3_EX) - #undef JSON_HEDLEY_CONCAT3_EX -#endif -#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c - -#if defined(JSON_HEDLEY_CONCAT3) - #undef JSON_HEDLEY_CONCAT3 -#endif -#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) - -#if defined(JSON_HEDLEY_VERSION_ENCODE) - #undef JSON_HEDLEY_VERSION_ENCODE -#endif -#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) - #undef JSON_HEDLEY_VERSION_DECODE_MAJOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) - #undef JSON_HEDLEY_VERSION_DECODE_MINOR -#endif -#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) - -#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) - #undef JSON_HEDLEY_VERSION_DECODE_REVISION -#endif -#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) - -#if defined(JSON_HEDLEY_GNUC_VERSION) - #undef JSON_HEDLEY_GNUC_VERSION -#endif -#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) - #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) -#elif defined(__GNUC__) - #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) -#endif - -#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) - #undef JSON_HEDLEY_GNUC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GNUC_VERSION) - #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION) - #undef JSON_HEDLEY_MSVC_VERSION -#endif -#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) - #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) -#elif defined(_MSC_FULL_VER) && !defined(__ICL) - #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) -#elif defined(_MSC_VER) && !defined(__ICL) - #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) - #undef JSON_HEDLEY_MSVC_VERSION_CHECK -#endif -#if !defined(JSON_HEDLEY_MSVC_VERSION) - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) -#elif defined(_MSC_VER) && (_MSC_VER >= 1400) - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) -#elif defined(_MSC_VER) && (_MSC_VER >= 1200) - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) -#else - #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION) - #undef JSON_HEDLEY_INTEL_VERSION -#endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) - #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) -#elif defined(__INTEL_COMPILER) && !defined(__ICL) - #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) -#endif - -#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) - #undef JSON_HEDLEY_INTEL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_INTEL_VERSION) - #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_INTEL_CL_VERSION) - #undef JSON_HEDLEY_INTEL_CL_VERSION -#endif -#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) - #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) -#endif - -#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) - #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_INTEL_CL_VERSION) - #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION) - #undef JSON_HEDLEY_PGI_VERSION -#endif -#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) - #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) -#endif - -#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) - #undef JSON_HEDLEY_PGI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PGI_VERSION) - #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION) - #undef JSON_HEDLEY_SUNPRO_VERSION -#endif -#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) -#elif defined(__SUNPRO_C) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) -#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) -#elif defined(__SUNPRO_CC) - #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) -#endif - -#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) - #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_SUNPRO_VERSION) - #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) - #undef JSON_HEDLEY_EMSCRIPTEN_VERSION -#endif -#if defined(__EMSCRIPTEN__) - #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) -#endif - -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) - #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) - #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION) - #undef JSON_HEDLEY_ARM_VERSION -#endif -#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) - #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) -#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) - #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) -#endif - -#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) - #undef JSON_HEDLEY_ARM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_ARM_VERSION) - #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION) - #undef JSON_HEDLEY_IBM_VERSION -#endif -#if defined(__ibmxl__) - #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) -#elif defined(__xlC__) && defined(__xlC_ver__) - #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) -#elif defined(__xlC__) - #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) -#endif - -#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) - #undef JSON_HEDLEY_IBM_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IBM_VERSION) - #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_VERSION) - #undef JSON_HEDLEY_TI_VERSION -#endif -#if \ - defined(__TI_COMPILER_VERSION__) && \ - ( \ - defined(__TMS470__) || defined(__TI_ARM__) || \ - defined(__MSP430__) || \ - defined(__TMS320C2000__) \ - ) -#if (__TI_COMPILER_VERSION__ >= 16000000) - #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif -#endif - -#if defined(JSON_HEDLEY_TI_VERSION_CHECK) - #undef JSON_HEDLEY_TI_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_VERSION) - #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL2000_VERSION) - #undef JSON_HEDLEY_TI_CL2000_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) - #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL2000_VERSION) - #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL430_VERSION) - #undef JSON_HEDLEY_TI_CL430_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) - #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL430_VERSION) - #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) - #undef JSON_HEDLEY_TI_ARMCL_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) - #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) - #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) - #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL6X_VERSION) - #undef JSON_HEDLEY_TI_CL6X_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) - #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL6X_VERSION) - #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CL7X_VERSION) - #undef JSON_HEDLEY_TI_CL7X_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) - #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CL7X_VERSION) - #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) - #undef JSON_HEDLEY_TI_CLPRU_VERSION -#endif -#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) - #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) -#endif - -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) - #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) - #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION) - #undef JSON_HEDLEY_CRAY_VERSION -#endif -#if defined(_CRAYC) - #if defined(_RELEASE_PATCHLEVEL) - #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) - #else - #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) - #endif -#endif - -#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) - #undef JSON_HEDLEY_CRAY_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_CRAY_VERSION) - #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION) - #undef JSON_HEDLEY_IAR_VERSION -#endif -#if defined(__IAR_SYSTEMS_ICC__) - #if __VER__ > 1000 - #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) - #else - #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) - #endif -#endif - -#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) - #undef JSON_HEDLEY_IAR_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_IAR_VERSION) - #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION) - #undef JSON_HEDLEY_TINYC_VERSION -#endif -#if defined(__TINYC__) - #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) -#endif - -#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) - #undef JSON_HEDLEY_TINYC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_TINYC_VERSION) - #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION) - #undef JSON_HEDLEY_DMC_VERSION -#endif -#if defined(__DMC__) - #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) -#endif - -#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) - #undef JSON_HEDLEY_DMC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_DMC_VERSION) - #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION) - #undef JSON_HEDLEY_COMPCERT_VERSION -#endif -#if defined(__COMPCERT_VERSION__) - #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) -#endif - -#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) - #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_COMPCERT_VERSION) - #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION) - #undef JSON_HEDLEY_PELLES_VERSION -#endif -#if defined(__POCC__) - #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) -#endif - -#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) - #undef JSON_HEDLEY_PELLES_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_PELLES_VERSION) - #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_MCST_LCC_VERSION) - #undef JSON_HEDLEY_MCST_LCC_VERSION -#endif -#if defined(__LCC__) && defined(__LCC_MINOR__) - #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) -#endif - -#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) - #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_MCST_LCC_VERSION) - #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION) - #undef JSON_HEDLEY_GCC_VERSION -#endif -#if \ - defined(JSON_HEDLEY_GNUC_VERSION) && \ - !defined(__clang__) && \ - !defined(JSON_HEDLEY_INTEL_VERSION) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_ARM_VERSION) && \ - !defined(JSON_HEDLEY_CRAY_VERSION) && \ - !defined(JSON_HEDLEY_TI_VERSION) && \ - !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ - !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ - !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ - !defined(__COMPCERT__) && \ - !defined(JSON_HEDLEY_MCST_LCC_VERSION) - #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION -#endif - -#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) - #undef JSON_HEDLEY_GCC_VERSION_CHECK -#endif -#if defined(JSON_HEDLEY_GCC_VERSION) - #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) -#else - #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) -#endif - -#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_HAS_ATTRIBUTE -#endif -#if \ - defined(__has_attribute) && \ - ( \ - (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ - ) -# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) -#else -# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) - #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) -#else - #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE -#endif -#if defined(__has_attribute) - #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) -#else - #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE -#endif -#if \ - defined(__has_cpp_attribute) && \ - defined(__cplusplus) && \ - (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) -#else - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) - #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS -#endif -#if !defined(__cplusplus) || !defined(__has_cpp_attribute) - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) -#elif \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_IAR_VERSION) && \ - (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ - (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) -#else - #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) - #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) -#else - #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE -#endif -#if defined(__has_cpp_attribute) && defined(__cplusplus) - #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) -#else - #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_BUILTIN) - #undef JSON_HEDLEY_HAS_BUILTIN -#endif -#if defined(__has_builtin) - #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) -#else - #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) - #undef JSON_HEDLEY_GNUC_HAS_BUILTIN -#endif -#if defined(__has_builtin) - #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) -#else - #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) - #undef JSON_HEDLEY_GCC_HAS_BUILTIN -#endif -#if defined(__has_builtin) - #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) -#else - #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_FEATURE) - #undef JSON_HEDLEY_HAS_FEATURE -#endif -#if defined(__has_feature) - #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) -#else - #define JSON_HEDLEY_HAS_FEATURE(feature) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) - #undef JSON_HEDLEY_GNUC_HAS_FEATURE -#endif -#if defined(__has_feature) - #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) -#else - #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) - #undef JSON_HEDLEY_GCC_HAS_FEATURE -#endif -#if defined(__has_feature) - #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) -#else - #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_EXTENSION) - #undef JSON_HEDLEY_HAS_EXTENSION -#endif -#if defined(__has_extension) - #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) -#else - #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) - #undef JSON_HEDLEY_GNUC_HAS_EXTENSION -#endif -#if defined(__has_extension) - #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) -#else - #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) - #undef JSON_HEDLEY_GCC_HAS_EXTENSION -#endif -#if defined(__has_extension) - #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) -#else - #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) - #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) -#else - #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) - #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) -#else - #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE -#endif -#if defined(__has_declspec_attribute) - #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) -#else - #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_HAS_WARNING) - #undef JSON_HEDLEY_HAS_WARNING -#endif -#if defined(__has_warning) - #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) -#else - #define JSON_HEDLEY_HAS_WARNING(warning) (0) -#endif - -#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) - #undef JSON_HEDLEY_GNUC_HAS_WARNING -#endif -#if defined(__has_warning) - #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) -#else - #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_GCC_HAS_WARNING) - #undef JSON_HEDLEY_GCC_HAS_WARNING -#endif -#if defined(__has_warning) - #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) -#else - #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ - defined(__clang__) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ - (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) - #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_PRAGMA(value) __pragma(value) -#else - #define JSON_HEDLEY_PRAGMA(value) -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) - #undef JSON_HEDLEY_DIAGNOSTIC_PUSH -#endif -#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) - #undef JSON_HEDLEY_DIAGNOSTIC_POP -#endif -#if defined(__clang__) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) - #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) -#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) - #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") - #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") -#else - #define JSON_HEDLEY_DIAGNOSTIC_PUSH - #define JSON_HEDLEY_DIAGNOSTIC_POP -#endif - -/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for - HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ -#endif -#if defined(__cplusplus) -# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") -# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") -# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ - _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ - xpr \ - JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ - xpr \ - JSON_HEDLEY_DIAGNOSTIC_POP -# endif -# else -# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ - xpr \ - JSON_HEDLEY_DIAGNOSTIC_POP -# endif -# endif -#endif -#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x -#endif - -#if defined(JSON_HEDLEY_CONST_CAST) - #undef JSON_HEDLEY_CONST_CAST -#endif -#if defined(__cplusplus) -# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) -#elif \ - JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ - ((T) (expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_REINTERPRET_CAST) - #undef JSON_HEDLEY_REINTERPRET_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) -#else - #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_STATIC_CAST) - #undef JSON_HEDLEY_STATIC_CAST -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) -#else - #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) -#endif - -#if defined(JSON_HEDLEY_CPP_CAST) - #undef JSON_HEDLEY_CPP_CAST -#endif -#if defined(__cplusplus) -# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") -# define JSON_HEDLEY_CPP_CAST(T, expr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ - ((T) (expr)) \ - JSON_HEDLEY_DIAGNOSTIC_POP -# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) -# define JSON_HEDLEY_CPP_CAST(T, expr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("diag_suppress=Pe137") \ - JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) -# endif -#else -# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") -#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") -#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") -#elif \ - JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") -#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL -#endif - -#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) - #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") -#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") -#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) -#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") -#else - #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION -#endif - -#if defined(JSON_HEDLEY_DEPRECATED) - #undef JSON_HEDLEY_DEPRECATED -#endif -#if defined(JSON_HEDLEY_DEPRECATED_FOR) - #undef JSON_HEDLEY_DEPRECATED_FOR -#endif -#if \ - JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) -#elif \ - (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) -#elif defined(__cplusplus) && (__cplusplus >= 201402L) - #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) -#elif \ - JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) - #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") -#else - #define JSON_HEDLEY_DEPRECATED(since) - #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) -#endif - -#if defined(JSON_HEDLEY_UNAVAILABLE) - #undef JSON_HEDLEY_UNAVAILABLE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) -#else - #define JSON_HEDLEY_UNAVAILABLE(available_since) -#endif - -#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) - #undef JSON_HEDLEY_WARN_UNUSED_RESULT -#endif -#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) - #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) -#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) - #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) - #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) -#elif defined(_Check_return_) /* SAL */ - #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ -#else - #define JSON_HEDLEY_WARN_UNUSED_RESULT - #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) -#endif - -#if defined(JSON_HEDLEY_SENTINEL) - #undef JSON_HEDLEY_SENTINEL -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) -#else - #define JSON_HEDLEY_SENTINEL(position) -#endif - -#if defined(JSON_HEDLEY_NO_RETURN) - #undef JSON_HEDLEY_NO_RETURN -#endif -#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_NO_RETURN __noreturn -#elif \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L - #define JSON_HEDLEY_NO_RETURN _Noreturn -#elif defined(__cplusplus) && (__cplusplus >= 201103L) - #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) -#elif \ - JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) - #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) - #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) - #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) -#else - #define JSON_HEDLEY_NO_RETURN -#endif - -#if defined(JSON_HEDLEY_NO_ESCAPE) - #undef JSON_HEDLEY_NO_ESCAPE -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) - #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) -#else - #define JSON_HEDLEY_NO_ESCAPE -#endif - -#if defined(JSON_HEDLEY_UNREACHABLE) - #undef JSON_HEDLEY_UNREACHABLE -#endif -#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) - #undef JSON_HEDLEY_UNREACHABLE_RETURN -#endif -#if defined(JSON_HEDLEY_ASSUME) - #undef JSON_HEDLEY_ASSUME -#endif -#if \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_ASSUME(expr) __assume(expr) -#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) - #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) -#elif \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) - #if defined(__cplusplus) - #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) - #else - #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) - #endif -#endif -#if \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() -#elif defined(JSON_HEDLEY_ASSUME) - #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) -#endif -#if !defined(JSON_HEDLEY_ASSUME) - #if defined(JSON_HEDLEY_UNREACHABLE) - #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) - #else - #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) - #endif -#endif -#if defined(JSON_HEDLEY_UNREACHABLE) - #if \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) - #else - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() - #endif -#else - #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) -#endif -#if !defined(JSON_HEDLEY_UNREACHABLE) - #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) -#endif - -JSON_HEDLEY_DIAGNOSTIC_PUSH -#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") - #pragma clang diagnostic ignored "-Wpedantic" -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) - #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" -#endif -#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) - #if defined(__clang__) - #pragma clang diagnostic ignored "-Wvariadic-macros" - #elif defined(JSON_HEDLEY_GCC_VERSION) - #pragma GCC diagnostic ignored "-Wvariadic-macros" - #endif -#endif -#if defined(JSON_HEDLEY_NON_NULL) - #undef JSON_HEDLEY_NON_NULL -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) - #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) -#else - #define JSON_HEDLEY_NON_NULL(...) -#endif -JSON_HEDLEY_DIAGNOSTIC_POP - -#if defined(JSON_HEDLEY_PRINTF_FORMAT) - #undef JSON_HEDLEY_PRINTF_FORMAT -#endif -#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) -#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) -#elif \ - JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) -#else - #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) -#endif - -#if defined(JSON_HEDLEY_CONSTEXPR) - #undef JSON_HEDLEY_CONSTEXPR -#endif -#if defined(__cplusplus) - #if __cplusplus >= 201103L - #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) - #endif -#endif -#if !defined(JSON_HEDLEY_CONSTEXPR) - #define JSON_HEDLEY_CONSTEXPR -#endif - -#if defined(JSON_HEDLEY_PREDICT) - #undef JSON_HEDLEY_PREDICT -#endif -#if defined(JSON_HEDLEY_LIKELY) - #undef JSON_HEDLEY_LIKELY -#endif -#if defined(JSON_HEDLEY_UNLIKELY) - #undef JSON_HEDLEY_UNLIKELY -#endif -#if defined(JSON_HEDLEY_UNPREDICTABLE) - #undef JSON_HEDLEY_UNPREDICTABLE -#endif -#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) - #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) -#endif -#if \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) -#elif \ - (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ - (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ - (__extension__ ({ \ - double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ - })) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ - (__extension__ ({ \ - double hedley_probability_ = (probability); \ - ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ - })) -# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) -# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) -#else -# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) -# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) -# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) -# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) -# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) -#endif -#if !defined(JSON_HEDLEY_UNPREDICTABLE) - #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) -#endif - -#if defined(JSON_HEDLEY_MALLOC) - #undef JSON_HEDLEY_MALLOC -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_MALLOC __declspec(restrict) -#else - #define JSON_HEDLEY_MALLOC -#endif - -#if defined(JSON_HEDLEY_PURE) - #undef JSON_HEDLEY_PURE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PURE __attribute__((__pure__)) -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) -# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") -#elif defined(__cplusplus) && \ - ( \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ - ) -# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") -#else -# define JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_CONST) - #undef JSON_HEDLEY_CONST -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_CONST __attribute__((__const__)) -#elif \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) - #define JSON_HEDLEY_CONST _Pragma("no_side_effect") -#else - #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE -#endif - -#if defined(JSON_HEDLEY_RESTRICT) - #undef JSON_HEDLEY_RESTRICT -#endif -#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) - #define JSON_HEDLEY_RESTRICT restrict -#elif \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ - defined(__clang__) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_RESTRICT __restrict -#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) - #define JSON_HEDLEY_RESTRICT _Restrict -#else - #define JSON_HEDLEY_RESTRICT -#endif - -#if defined(JSON_HEDLEY_INLINE) - #undef JSON_HEDLEY_INLINE -#endif -#if \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ - (defined(__cplusplus) && (__cplusplus >= 199711L)) - #define JSON_HEDLEY_INLINE inline -#elif \ - defined(JSON_HEDLEY_GCC_VERSION) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) - #define JSON_HEDLEY_INLINE __inline__ -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_INLINE __inline -#else - #define JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_ALWAYS_INLINE) - #undef JSON_HEDLEY_ALWAYS_INLINE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) -# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_ALWAYS_INLINE __forceinline -#elif defined(__cplusplus) && \ - ( \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ - ) -# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") -#else -# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE -#endif - -#if defined(JSON_HEDLEY_NEVER_INLINE) - #undef JSON_HEDLEY_NEVER_INLINE -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ - JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ - (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ - (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ - (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ - JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ - JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ - JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) - #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) - #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") -#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) - #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) - #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") -#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) - #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) - #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) -#else - #define JSON_HEDLEY_NEVER_INLINE -#endif - -#if defined(JSON_HEDLEY_PRIVATE) - #undef JSON_HEDLEY_PRIVATE -#endif -#if defined(JSON_HEDLEY_PUBLIC) - #undef JSON_HEDLEY_PUBLIC -#endif -#if defined(JSON_HEDLEY_IMPORT) - #undef JSON_HEDLEY_IMPORT -#endif -#if defined(_WIN32) || defined(__CYGWIN__) -# define JSON_HEDLEY_PRIVATE -# define JSON_HEDLEY_PUBLIC __declspec(dllexport) -# define JSON_HEDLEY_IMPORT __declspec(dllimport) -#else -# if \ - JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - ( \ - defined(__TI_EABI__) && \ - ( \ - (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ - ) \ - ) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) -# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) -# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) -# else -# define JSON_HEDLEY_PRIVATE -# define JSON_HEDLEY_PUBLIC -# endif -# define JSON_HEDLEY_IMPORT extern -#endif - -#if defined(JSON_HEDLEY_NO_THROW) - #undef JSON_HEDLEY_NO_THROW -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) - #define JSON_HEDLEY_NO_THROW __declspec(nothrow) -#else - #define JSON_HEDLEY_NO_THROW -#endif - -#if defined(JSON_HEDLEY_FALL_THROUGH) - #undef JSON_HEDLEY_FALL_THROUGH -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) - #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) -#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) - #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) -#elif defined(__fallthrough) /* SAL */ - #define JSON_HEDLEY_FALL_THROUGH __fallthrough -#else - #define JSON_HEDLEY_FALL_THROUGH -#endif - -#if defined(JSON_HEDLEY_RETURNS_NON_NULL) - #undef JSON_HEDLEY_RETURNS_NON_NULL -#endif -#if \ - JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) -#elif defined(_Ret_notnull_) /* SAL */ - #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ -#else - #define JSON_HEDLEY_RETURNS_NON_NULL -#endif - -#if defined(JSON_HEDLEY_ARRAY_PARAM) - #undef JSON_HEDLEY_ARRAY_PARAM -#endif -#if \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ - !defined(__STDC_NO_VLA__) && \ - !defined(__cplusplus) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_TINYC_VERSION) - #define JSON_HEDLEY_ARRAY_PARAM(name) (name) -#else - #define JSON_HEDLEY_ARRAY_PARAM(name) -#endif - -#if defined(JSON_HEDLEY_IS_CONSTANT) - #undef JSON_HEDLEY_IS_CONSTANT -#endif -#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) - #undef JSON_HEDLEY_REQUIRE_CONSTEXPR -#endif -/* JSON_HEDLEY_IS_CONSTEXPR_ is for - HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ -#if defined(JSON_HEDLEY_IS_CONSTEXPR_) - #undef JSON_HEDLEY_IS_CONSTEXPR_ -#endif -#if \ - JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ - (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) - #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) -#endif -#if !defined(__cplusplus) -# if \ - JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ - JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ - JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) -#if defined(__INTPTR_TYPE__) - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) -#else - #include - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) -#endif -# elif \ - ( \ - defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ - !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ - !defined(JSON_HEDLEY_PGI_VERSION) && \ - !defined(JSON_HEDLEY_IAR_VERSION)) || \ - (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ - JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ - JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) -#if defined(__INTPTR_TYPE__) - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) -#else - #include - #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) -#endif -# elif \ - defined(JSON_HEDLEY_GCC_VERSION) || \ - defined(JSON_HEDLEY_INTEL_VERSION) || \ - defined(JSON_HEDLEY_TINYC_VERSION) || \ - defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ - JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ - defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ - defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ - defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ - defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ - defined(__clang__) -# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ - sizeof(void) != \ - sizeof(*( \ - 1 ? \ - ((void*) ((expr) * 0L) ) : \ -((struct { char v[sizeof(void) * 2]; } *) 1) \ - ) \ - ) \ - ) -# endif -#endif -#if defined(JSON_HEDLEY_IS_CONSTEXPR_) - #if !defined(JSON_HEDLEY_IS_CONSTANT) - #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) - #endif - #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) -#else - #if !defined(JSON_HEDLEY_IS_CONSTANT) - #define JSON_HEDLEY_IS_CONSTANT(expr) (0) - #endif - #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) -#endif - -#if defined(JSON_HEDLEY_BEGIN_C_DECLS) - #undef JSON_HEDLEY_BEGIN_C_DECLS -#endif -#if defined(JSON_HEDLEY_END_C_DECLS) - #undef JSON_HEDLEY_END_C_DECLS -#endif -#if defined(JSON_HEDLEY_C_DECL) - #undef JSON_HEDLEY_C_DECL -#endif -#if defined(__cplusplus) - #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { - #define JSON_HEDLEY_END_C_DECLS } - #define JSON_HEDLEY_C_DECL extern "C" -#else - #define JSON_HEDLEY_BEGIN_C_DECLS - #define JSON_HEDLEY_END_C_DECLS - #define JSON_HEDLEY_C_DECL -#endif - -#if defined(JSON_HEDLEY_STATIC_ASSERT) - #undef JSON_HEDLEY_STATIC_ASSERT -#endif -#if \ - !defined(__cplusplus) && ( \ - (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ - (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ - JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ - defined(_Static_assert) \ - ) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) -#elif \ - (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ - JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) -#else -# define JSON_HEDLEY_STATIC_ASSERT(expr, message) -#endif - -#if defined(JSON_HEDLEY_NULL) - #undef JSON_HEDLEY_NULL -#endif -#if defined(__cplusplus) - #if __cplusplus >= 201103L - #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) - #elif defined(NULL) - #define JSON_HEDLEY_NULL NULL - #else - #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) - #endif -#elif defined(NULL) - #define JSON_HEDLEY_NULL NULL -#else - #define JSON_HEDLEY_NULL ((void*) 0) -#endif - -#if defined(JSON_HEDLEY_MESSAGE) - #undef JSON_HEDLEY_MESSAGE -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -# define JSON_HEDLEY_MESSAGE(msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ - JSON_HEDLEY_PRAGMA(message msg) \ - JSON_HEDLEY_DIAGNOSTIC_POP -#elif \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) -#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) -#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) -# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -# define JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_WARNING) - #undef JSON_HEDLEY_WARNING -#endif -#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") -# define JSON_HEDLEY_WARNING(msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ - JSON_HEDLEY_PRAGMA(clang warning msg) \ - JSON_HEDLEY_DIAGNOSTIC_POP -#elif \ - JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ - JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ - JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) -#elif \ - JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) -#else -# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) -#endif - -#if defined(JSON_HEDLEY_REQUIRE) - #undef JSON_HEDLEY_REQUIRE -#endif -#if defined(JSON_HEDLEY_REQUIRE_MSG) - #undef JSON_HEDLEY_REQUIRE_MSG -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) -# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") -# define JSON_HEDLEY_REQUIRE(expr) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ - __attribute__((diagnose_if(!(expr), #expr, "error"))) \ - JSON_HEDLEY_DIAGNOSTIC_POP -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ - __attribute__((diagnose_if(!(expr), msg, "error"))) \ - JSON_HEDLEY_DIAGNOSTIC_POP -# else -# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) -# endif -#else -# define JSON_HEDLEY_REQUIRE(expr) -# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) -#endif - -#if defined(JSON_HEDLEY_FLAGS) - #undef JSON_HEDLEY_FLAGS -#endif -#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) - #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) -#else - #define JSON_HEDLEY_FLAGS -#endif - -#if defined(JSON_HEDLEY_FLAGS_CAST) - #undef JSON_HEDLEY_FLAGS_CAST -#endif -#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) -# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ - JSON_HEDLEY_DIAGNOSTIC_PUSH \ - _Pragma("warning(disable:188)") \ - ((T) (expr)); \ - JSON_HEDLEY_DIAGNOSTIC_POP \ - })) -#else -# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) -#endif - -#if defined(JSON_HEDLEY_EMPTY_BASES) - #undef JSON_HEDLEY_EMPTY_BASES -#endif -#if \ - (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ - JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) - #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) -#else - #define JSON_HEDLEY_EMPTY_BASES -#endif - -/* Remaining macros are deprecated. */ - -#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) - #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK -#endif -#if defined(__clang__) - #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) -#else - #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) -#endif - -#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) - #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) - #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) - #undef JSON_HEDLEY_CLANG_HAS_BUILTIN -#endif -#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) - -#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) - #undef JSON_HEDLEY_CLANG_HAS_FEATURE -#endif -#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) - -#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) - #undef JSON_HEDLEY_CLANG_HAS_EXTENSION -#endif -#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) - -#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) - #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE -#endif -#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) - -#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) - #undef JSON_HEDLEY_CLANG_HAS_WARNING -#endif -#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) - -#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ - - -// This file contains all internal macro definitions (except those affecting ABI) -// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them - -// #include - - -// exclude unsupported compilers -#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) - #if defined(__clang__) - #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 - #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) - #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 - #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" - #endif - #endif -#endif - -// C++ language standard detection -// if the user manually specified the used c++ version this is skipped -#if !defined(JSON_HAS_CPP_23) && !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) - #if (defined(__cplusplus) && __cplusplus > 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG > 202002L) - #define JSON_HAS_CPP_23 - #define JSON_HAS_CPP_20 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus > 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG > 201703L) - #define JSON_HAS_CPP_20 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus > 201402L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 - #define JSON_HAS_CPP_17 - #define JSON_HAS_CPP_14 - #elif (defined(__cplusplus) && __cplusplus > 201103L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) - #define JSON_HAS_CPP_14 - #endif - // the cpp 11 flag is always specified because it is the minimal required version - #define JSON_HAS_CPP_11 -#endif - -#ifdef __has_include - #if __has_include() - #include - #endif -#endif - -#if !defined(JSON_HAS_FILESYSTEM) && !defined(JSON_HAS_EXPERIMENTAL_FILESYSTEM) - #ifdef JSON_HAS_CPP_17 - #if defined(__cpp_lib_filesystem) - #define JSON_HAS_FILESYSTEM 1 - #elif defined(__cpp_lib_experimental_filesystem) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif !defined(__has_include) - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_FILESYSTEM 1 - #elif __has_include() - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 1 - #endif - - // std::filesystem does not work on MinGW GCC 8: https://sourceforge.net/p/mingw-w64/bugs/737/ - #if defined(__MINGW32__) && defined(__GNUC__) && __GNUC__ == 8 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before GCC 8: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 8 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before Clang 7: https://en.cppreference.com/w/cpp/compiler_support - #if defined(__clang_major__) && __clang_major__ < 7 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before MSVC 19.14: https://en.cppreference.com/w/cpp/compiler_support - #if defined(_MSC_VER) && _MSC_VER < 1914 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before iOS 13 - #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - - // no filesystem support before macOS Catalina - #if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 - #undef JSON_HAS_FILESYSTEM - #undef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #endif - #endif -#endif - -#ifndef JSON_HAS_EXPERIMENTAL_FILESYSTEM - #define JSON_HAS_EXPERIMENTAL_FILESYSTEM 0 -#endif - -#ifndef JSON_HAS_FILESYSTEM - #define JSON_HAS_FILESYSTEM 0 -#endif - -#ifndef JSON_HAS_THREE_WAY_COMPARISON - #if defined(__cpp_impl_three_way_comparison) && __cpp_impl_three_way_comparison >= 201907L \ - && defined(__cpp_lib_three_way_comparison) && __cpp_lib_three_way_comparison >= 201907L - #define JSON_HAS_THREE_WAY_COMPARISON 1 - #else - #define JSON_HAS_THREE_WAY_COMPARISON 0 - #endif -#endif - -#ifndef JSON_HAS_RANGES - // ranges header shipping in GCC 11.1.0 (released 2021-04-27) has syntax error - #if defined(__GLIBCXX__) && __GLIBCXX__ == 20210427 - #define JSON_HAS_RANGES 0 - #elif defined(__cpp_lib_ranges) - #define JSON_HAS_RANGES 1 - #else - #define JSON_HAS_RANGES 0 - #endif -#endif - -#ifndef JSON_HAS_STATIC_RTTI - #if !defined(_HAS_STATIC_RTTI) || _HAS_STATIC_RTTI != 0 - #define JSON_HAS_STATIC_RTTI 1 - #else - #define JSON_HAS_STATIC_RTTI 0 - #endif -#endif - -#ifdef JSON_HAS_CPP_17 - #define JSON_INLINE_VARIABLE inline -#else - #define JSON_INLINE_VARIABLE -#endif - -#if JSON_HEDLEY_HAS_ATTRIBUTE(no_unique_address) - #define JSON_NO_UNIQUE_ADDRESS [[no_unique_address]] -#else - #define JSON_NO_UNIQUE_ADDRESS -#endif - -// disable documentation warnings on clang -#if defined(__clang__) - #pragma clang diagnostic push - #pragma clang diagnostic ignored "-Wdocumentation" - #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" -#endif - -// allow disabling exceptions -#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) - #define JSON_THROW(exception) throw exception - #define JSON_TRY try - #define JSON_CATCH(exception) catch(exception) - #define JSON_INTERNAL_CATCH(exception) catch(exception) -#else - #include - #define JSON_THROW(exception) std::abort() - #define JSON_TRY if(true) - #define JSON_CATCH(exception) if(false) - #define JSON_INTERNAL_CATCH(exception) if(false) -#endif - -// override exception macros -#if defined(JSON_THROW_USER) - #undef JSON_THROW - #define JSON_THROW JSON_THROW_USER -#endif -#if defined(JSON_TRY_USER) - #undef JSON_TRY - #define JSON_TRY JSON_TRY_USER -#endif -#if defined(JSON_CATCH_USER) - #undef JSON_CATCH - #define JSON_CATCH JSON_CATCH_USER - #undef JSON_INTERNAL_CATCH - #define JSON_INTERNAL_CATCH JSON_CATCH_USER -#endif -#if defined(JSON_INTERNAL_CATCH_USER) - #undef JSON_INTERNAL_CATCH - #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER -#endif - -// allow overriding assert -#if !defined(JSON_ASSERT) - #include // assert - #define JSON_ASSERT(x) assert(x) -#endif - -// allow to access some private functions (needed by the test suite) -#if defined(JSON_TESTS_PRIVATE) - #define JSON_PRIVATE_UNLESS_TESTED public -#else - #define JSON_PRIVATE_UNLESS_TESTED private -#endif - -/*! -@brief macro to briefly define a mapping between an enum and JSON -@def NLOHMANN_JSON_SERIALIZE_ENUM -@since version 3.4.0 -*/ -#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ - template \ - inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ - { \ - /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [e](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.first == e; \ - }); \ - j = ((it != std::end(m)) ? it : std::begin(m))->second; \ - } \ - template \ - inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ - { \ - /* NOLINTNEXTLINE(modernize-type-traits) we use C++11 */ \ - static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ - /* NOLINTNEXTLINE(modernize-avoid-c-arrays) we don't want to depend on */ \ - static const std::pair m[] = __VA_ARGS__; \ - auto it = std::find_if(std::begin(m), std::end(m), \ - [&j](const std::pair& ej_pair) -> bool \ - { \ - return ej_pair.second == j; \ - }); \ - e = ((it != std::end(m)) ? it : std::begin(m))->first; \ - } - -// Ugly macros to avoid uglier copy-paste when specializing basic_json. They -// may be removed in the future once the class is split. - -#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ - template class ObjectType, \ - template class ArrayType, \ - class StringType, class BooleanType, class NumberIntegerType, \ - class NumberUnsignedType, class NumberFloatType, \ - template class AllocatorType, \ - template class JSONSerializer, \ - class BinaryType, \ - class CustomBaseClass> - -#define NLOHMANN_BASIC_JSON_TPL \ - basic_json - -// Macros to simplify conversion from/to types - -#define NLOHMANN_JSON_EXPAND( x ) x -#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME -#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ - NLOHMANN_JSON_PASTE64, \ - NLOHMANN_JSON_PASTE63, \ - NLOHMANN_JSON_PASTE62, \ - NLOHMANN_JSON_PASTE61, \ - NLOHMANN_JSON_PASTE60, \ - NLOHMANN_JSON_PASTE59, \ - NLOHMANN_JSON_PASTE58, \ - NLOHMANN_JSON_PASTE57, \ - NLOHMANN_JSON_PASTE56, \ - NLOHMANN_JSON_PASTE55, \ - NLOHMANN_JSON_PASTE54, \ - NLOHMANN_JSON_PASTE53, \ - NLOHMANN_JSON_PASTE52, \ - NLOHMANN_JSON_PASTE51, \ - NLOHMANN_JSON_PASTE50, \ - NLOHMANN_JSON_PASTE49, \ - NLOHMANN_JSON_PASTE48, \ - NLOHMANN_JSON_PASTE47, \ - NLOHMANN_JSON_PASTE46, \ - NLOHMANN_JSON_PASTE45, \ - NLOHMANN_JSON_PASTE44, \ - NLOHMANN_JSON_PASTE43, \ - NLOHMANN_JSON_PASTE42, \ - NLOHMANN_JSON_PASTE41, \ - NLOHMANN_JSON_PASTE40, \ - NLOHMANN_JSON_PASTE39, \ - NLOHMANN_JSON_PASTE38, \ - NLOHMANN_JSON_PASTE37, \ - NLOHMANN_JSON_PASTE36, \ - NLOHMANN_JSON_PASTE35, \ - NLOHMANN_JSON_PASTE34, \ - NLOHMANN_JSON_PASTE33, \ - NLOHMANN_JSON_PASTE32, \ - NLOHMANN_JSON_PASTE31, \ - NLOHMANN_JSON_PASTE30, \ - NLOHMANN_JSON_PASTE29, \ - NLOHMANN_JSON_PASTE28, \ - NLOHMANN_JSON_PASTE27, \ - NLOHMANN_JSON_PASTE26, \ - NLOHMANN_JSON_PASTE25, \ - NLOHMANN_JSON_PASTE24, \ - NLOHMANN_JSON_PASTE23, \ - NLOHMANN_JSON_PASTE22, \ - NLOHMANN_JSON_PASTE21, \ - NLOHMANN_JSON_PASTE20, \ - NLOHMANN_JSON_PASTE19, \ - NLOHMANN_JSON_PASTE18, \ - NLOHMANN_JSON_PASTE17, \ - NLOHMANN_JSON_PASTE16, \ - NLOHMANN_JSON_PASTE15, \ - NLOHMANN_JSON_PASTE14, \ - NLOHMANN_JSON_PASTE13, \ - NLOHMANN_JSON_PASTE12, \ - NLOHMANN_JSON_PASTE11, \ - NLOHMANN_JSON_PASTE10, \ - NLOHMANN_JSON_PASTE9, \ - NLOHMANN_JSON_PASTE8, \ - NLOHMANN_JSON_PASTE7, \ - NLOHMANN_JSON_PASTE6, \ - NLOHMANN_JSON_PASTE5, \ - NLOHMANN_JSON_PASTE4, \ - NLOHMANN_JSON_PASTE3, \ - NLOHMANN_JSON_PASTE2, \ - NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) -#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) -#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) -#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) -#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) -#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) -#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) -#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) -#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) -#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) -#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) -#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) -#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) -#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) -#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) -#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) -#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) -#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) -#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) -#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) -#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) -#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) -#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) -#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) -#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) -#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) -#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) -#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) -#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) -#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) -#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) -#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) -#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) -#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) -#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) -#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) -#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) -#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) -#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) -#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) -#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) -#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) -#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) -#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) -#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) -#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) -#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) -#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) -#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) -#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) -#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) -#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) -#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) -#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) -#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) -#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) -#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) -#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) -#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) -#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) -#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) -#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) -#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) - -#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; -#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); -#define NLOHMANN_JSON_FROM_WITH_DEFAULT(v1) nlohmann_json_t.v1 = !nlohmann_json_j.is_null() ? nlohmann_json_j.value(#v1, nlohmann_json_default_obj.v1) : nlohmann_json_default_obj.v1; - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_INTRUSIVE -@since version 3.9.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ -*/ -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ - template::value, int> = 0> \ - friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - template::value, int> = 0> \ - friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT -@since version 3.11.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ -*/ -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - template::value, int> = 0> \ - friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - template::value, int> = 0> \ - friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE -@since version 3.11.3 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_intrusive/ -*/ -#define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ - template::value, int> = 0> \ - friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE -@since version 3.9.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ -*/ -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ - template::value, int> = 0> \ - void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - template::value, int> = 0> \ - void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT -@since version 3.11.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ -*/ -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, ...) \ - template::value, int> = 0> \ - void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - template::value, int> = 0> \ - void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE -@since version 3.11.3 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_type_non_intrusive/ -*/ -#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, ...) \ - template::value, int> = 0> \ - void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE -@since version 3.12.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ -*/ -#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE(Type, BaseType, ...) \ - template::value, int> = 0> \ - friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - template::value, int> = 0> \ - friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT -@since version 3.12.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ -*/ -#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ - template::value, int> = 0> \ - friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - template::value, int> = 0> \ - friend void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE -@since version 3.12.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ -*/ -#define NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ - template::value, int> = 0> \ - friend void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE -@since version 3.12.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ -*/ -#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE(Type, BaseType, ...) \ - template::value, int> = 0> \ - void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - template::value, int> = 0> \ - void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT -@since version 3.12.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ -*/ -#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Type, BaseType, ...) \ - template::value, int> = 0> \ - void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ - template::value, int> = 0> \ - void from_json(const BasicJsonType& nlohmann_json_j, Type& nlohmann_json_t) { nlohmann::from_json(nlohmann_json_j, static_cast(nlohmann_json_t)); const Type nlohmann_json_default_obj{}; NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM_WITH_DEFAULT, __VA_ARGS__)) } - -/*! -@brief macro -@def NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE -@since version 3.12.0 -@sa https://json.nlohmann.me/api/macros/nlohmann_define_derived_type/ -*/ -#define NLOHMANN_DEFINE_DERIVED_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(Type, BaseType, ...) \ - template::value, int> = 0> \ - void to_json(BasicJsonType& nlohmann_json_j, const Type& nlohmann_json_t) { nlohmann::to_json(nlohmann_json_j, static_cast(nlohmann_json_t)); NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } - -// inspired from https://stackoverflow.com/a/26745591 -// allows calling any std function as if (e.g., with begin): -// using std::begin; begin(x); -// -// it allows using the detected idiom to retrieve the return type -// of such an expression -#define NLOHMANN_CAN_CALL_STD_FUNC_IMPL(std_name) \ - namespace detail { \ - using std::std_name; \ - \ - template \ - using result_of_##std_name = decltype(std_name(std::declval()...)); \ - } \ - \ - namespace detail2 { \ - struct std_name##_tag \ - { \ - }; \ - \ - template \ - std_name##_tag std_name(T&&...); \ - \ - template \ - using result_of_##std_name = decltype(std_name(std::declval()...)); \ - \ - template \ - struct would_call_std_##std_name \ - { \ - static constexpr auto const value = ::nlohmann::detail:: \ - is_detected_exact::value; \ - }; \ - } /* namespace detail2 */ \ - \ - template \ - struct would_call_std_##std_name : detail2::would_call_std_##std_name \ - { \ - } - -#ifndef JSON_USE_IMPLICIT_CONVERSIONS - #define JSON_USE_IMPLICIT_CONVERSIONS 1 -#endif - -#if JSON_USE_IMPLICIT_CONVERSIONS - #define JSON_EXPLICIT -#else - #define JSON_EXPLICIT explicit -#endif - -#ifndef JSON_DISABLE_ENUM_SERIALIZATION - #define JSON_DISABLE_ENUM_SERIALIZATION 0 -#endif - -#ifndef JSON_USE_GLOBAL_UDLS - #define JSON_USE_GLOBAL_UDLS 1 -#endif - -#if JSON_HAS_THREE_WAY_COMPARISON - #include // partial_ordering -#endif - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/////////////////////////// -// JSON type enumeration // -/////////////////////////// - -/*! -@brief the JSON type enumeration - -This enumeration collects the different JSON types. It is internally used to -distinguish the stored values, and the functions @ref basic_json::is_null(), -@ref basic_json::is_object(), @ref basic_json::is_array(), -@ref basic_json::is_string(), @ref basic_json::is_boolean(), -@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), -@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), -@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and -@ref basic_json::is_structured() rely on it. - -@note There are three enumeration entries (number_integer, number_unsigned, and -number_float), because the library distinguishes these three types for numbers: -@ref basic_json::number_unsigned_t is used for unsigned integers, -@ref basic_json::number_integer_t is used for signed integers, and -@ref basic_json::number_float_t is used for floating-point numbers or to -approximate integers which do not fit in the limits of their respective type. - -@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON -value with the default value for a given type - -@since version 1.0.0 -*/ -enum class value_t : std::uint8_t -{ - null, ///< null value - object, ///< object (unordered set of name/value pairs) - array, ///< array (ordered collection of values) - string, ///< string value - boolean, ///< boolean value - number_integer, ///< number value (signed integer) - number_unsigned, ///< number value (unsigned integer) - number_float, ///< number value (floating-point) - binary, ///< binary array (ordered collection of bytes) - discarded ///< discarded by the parser callback function -}; - -/*! -@brief comparison operator for JSON types - -Returns an ordering that is similar to Python: -- order: null < boolean < number < object < array < string < binary -- furthermore, each type is not smaller than itself -- discarded values are not comparable -- binary is represented as a b"" string in python and directly comparable to a - string; however, making a binary array directly comparable with a string would - be surprising behavior in a JSON file. - -@since version 1.0.0 -*/ -#if JSON_HAS_THREE_WAY_COMPARISON - inline std::partial_ordering operator<=>(const value_t lhs, const value_t rhs) noexcept // *NOPAD* -#else - inline bool operator<(const value_t lhs, const value_t rhs) noexcept -#endif -{ - static constexpr std::array order = {{ - 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, - 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, - 6 /* binary */ - } - }; - - const auto l_index = static_cast(lhs); - const auto r_index = static_cast(rhs); -#if JSON_HAS_THREE_WAY_COMPARISON - if (l_index < order.size() && r_index < order.size()) - { - return order[l_index] <=> order[r_index]; // *NOPAD* - } - return std::partial_ordering::unordered; -#else - return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; -#endif -} - -// GCC selects the built-in operator< over an operator rewritten from -// a user-defined spaceship operator -// Clang, MSVC, and ICC select the rewritten candidate -// (see GCC bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105200) -#if JSON_HAS_THREE_WAY_COMPARISON && defined(__GNUC__) -inline bool operator<(const value_t lhs, const value_t rhs) noexcept -{ - return std::is_lt(lhs <=> rhs); // *NOPAD* -} -#endif - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/*! -@brief replace all occurrences of a substring by another string - -@param[in,out] s the string to manipulate; changed so that all - occurrences of @a f are replaced with @a t -@param[in] f the substring to replace with @a t -@param[in] t the string to replace @a f - -@pre The search string @a f must not be empty. **This precondition is -enforced with an assertion.** - -@since version 2.0.0 -*/ -template -inline void replace_substring(StringType& s, const StringType& f, - const StringType& t) -{ - JSON_ASSERT(!f.empty()); - for (auto pos = s.find(f); // find first occurrence of f - pos != StringType::npos; // make sure f was found - s.replace(pos, f.size(), t), // replace with t, and - pos = s.find(f, pos + t.size())) // find next occurrence of f - {} -} - -/*! - * @brief string escaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to escape - * @return escaped string - * - * Note the order of escaping "~" to "~0" and "/" to "~1" is important. - */ -template -inline StringType escape(StringType s) -{ - replace_substring(s, StringType{"~"}, StringType{"~0"}); - replace_substring(s, StringType{"/"}, StringType{"~1"}); - return s; -} - -/*! - * @brief string unescaping as described in RFC 6901 (Sect. 4) - * @param[in] s string to unescape - * @return unescaped string - * - * Note the order of escaping "~1" to "/" and "~0" to "~" is important. - */ -template -static void unescape(StringType& s) -{ - replace_substring(s, StringType{"~1"}, StringType{"/"}); - replace_substring(s, StringType{"~0"}, StringType{"~"}); -} - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // size_t - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -/// struct to capture the start position of the current token -struct position_t -{ - /// the total number of characters read - std::size_t chars_read_total = 0; - /// the number of characters read in the current line - std::size_t chars_read_current_line = 0; - /// the number of lines read - std::size_t lines_read = 0; - - /// conversion to size_t to preserve SAX interface - constexpr operator size_t() const - { - return chars_read_total; - } -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-FileCopyrightText: 2018 The Abseil Authors -// SPDX-License-Identifier: MIT - - - -#include // array -#include // size_t -#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type -#include // index_sequence, make_index_sequence, index_sequence_for - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -using uncvref_t = typename std::remove_cv::type>::type; - -#ifdef JSON_HAS_CPP_14 - -// the following utilities are natively available in C++14 -using std::enable_if_t; -using std::index_sequence; -using std::make_index_sequence; -using std::index_sequence_for; - -#else - -// alias templates to reduce boilerplate -template -using enable_if_t = typename std::enable_if::type; - -// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h -// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. - -//// START OF CODE FROM GOOGLE ABSEIL - -// integer_sequence -// -// Class template representing a compile-time integer sequence. An instantiation -// of `integer_sequence` has a sequence of integers encoded in its -// type through its template arguments (which is a common need when -// working with C++11 variadic templates). `absl::integer_sequence` is designed -// to be a drop-in replacement for C++14's `std::integer_sequence`. -// -// Example: -// -// template< class T, T... Ints > -// void user_function(integer_sequence); -// -// int main() -// { -// // user_function's `T` will be deduced to `int` and `Ints...` -// // will be deduced to `0, 1, 2, 3, 4`. -// user_function(make_integer_sequence()); -// } -template -struct integer_sequence -{ - using value_type = T; - static constexpr std::size_t size() noexcept - { - return sizeof...(Ints); - } -}; - -// index_sequence -// -// A helper template for an `integer_sequence` of `size_t`, -// `absl::index_sequence` is designed to be a drop-in replacement for C++14's -// `std::index_sequence`. -template -using index_sequence = integer_sequence; - -namespace utility_internal -{ - -template -struct Extend; - -// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. -template -struct Extend, SeqSize, 0> -{ - using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; -}; - -template -struct Extend, SeqSize, 1> -{ - using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; -}; - -// Recursion helper for 'make_integer_sequence'. -// 'Gen::type' is an alias for 'integer_sequence'. -template -struct Gen -{ - using type = - typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; -}; - -template -struct Gen -{ - using type = integer_sequence; -}; - -} // namespace utility_internal - -// Compile-time sequences of integers - -// make_integer_sequence -// -// This template alias is equivalent to -// `integer_sequence`, and is designed to be a drop-in -// replacement for C++14's `std::make_integer_sequence`. -template -using make_integer_sequence = typename utility_internal::Gen::type; - -// make_index_sequence -// -// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, -// and is designed to be a drop-in replacement for C++14's -// `std::make_index_sequence`. -template -using make_index_sequence = make_integer_sequence; - -// index_sequence_for -// -// Converts a typename pack into an index sequence of the same length, and -// is designed to be a drop-in replacement for C++14's -// `std::index_sequence_for()` -template -using index_sequence_for = make_index_sequence; - -//// END OF CODE FROM GOOGLE ABSEIL - -#endif - -// dispatch utility (taken from ranges-v3) -template struct priority_tag : priority_tag < N - 1 > {}; -template<> struct priority_tag<0> {}; - -// taken from ranges-v3 -template -struct static_const -{ - static JSON_INLINE_VARIABLE constexpr T value{}; -}; - -#ifndef JSON_HAS_CPP_17 - template - constexpr T static_const::value; -#endif - -template -constexpr std::array make_array(Args&& ... args) -{ - return std::array {{static_cast(std::forward(args))...}}; -} - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // numeric_limits -#include // char_traits -#include // tuple -#include // false_type, is_constructible, is_integral, is_same, true_type -#include // declval - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -#include // random_access_iterator_tag - -// #include - -// #include - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN -namespace detail -{ - -template -struct iterator_types {}; - -template -struct iterator_types < - It, - void_t> -{ - using difference_type = typename It::difference_type; - using value_type = typename It::value_type; - using pointer = typename It::pointer; - using reference = typename It::reference; - using iterator_category = typename It::iterator_category; -}; - -// This is required as some compilers implement std::iterator_traits in a way that -// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. -template -struct iterator_traits -{ -}; - -template -struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> - : iterator_types -{ -}; - -template -struct iterator_traits::value>> -{ - using iterator_category = std::random_access_iterator_tag; - using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using reference = T&; -}; - -} // namespace detail -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN - -NLOHMANN_CAN_CALL_STD_FUNC_IMPL(begin); - -NLOHMANN_JSON_NAMESPACE_END - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - - - -// #include - - -NLOHMANN_JSON_NAMESPACE_BEGIN - -NLOHMANN_CAN_CALL_STD_FUNC_IMPL(end); - -NLOHMANN_JSON_NAMESPACE_END - -// #include - -// #include - -// #include -// __ _____ _____ _____ -// __| | __| | | | JSON for Modern C++ -// | | |__ | | | | | | version 3.12.0 -// |_____|_____|_____|_|___| https://github.com/nlohmann/json -// -// SPDX-FileCopyrightText: 2013 - 2025 Niels Lohmann -// SPDX-License-Identifier: MIT - -#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ - #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ - - #include // int64_t, uint64_t - #include // map - #include // allocator - #include // string - #include // vector - - // #include - - - /*! - @brief namespace for Niels Lohmann - @see https://github.com/nlohmann - @since version 1.0.0 - */ - NLOHMANN_JSON_NAMESPACE_BEGIN - - /*! - @brief default JSONSerializer template argument - - This serializer ignores the template arguments and uses ADL - ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) - for serialization. - */ - template - struct adl_serializer; - - /// a class to store JSON values - /// @sa https://json.nlohmann.me/api/basic_json/ - template class ObjectType = - std::map, - template class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator, - template class JSONSerializer = - adl_serializer, - class BinaryType = std::vector, // cppcheck-suppress syntaxError - class CustomBaseClass = void> - class basic_json; - - /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document - /// @sa https://json.nlohmann.me/api/json_pointer/ - template - class json_pointer; - - /*! - @brief default specialization - @sa https://json.nlohmann.me/api/json/ - */ - using json = basic_json<>; - - /// @brief a minimal map-like container that preserves insertion order - /// @sa https://json.nlohmann.me/api/ordered_map/ - template - struct ordered_map; - - /// @brief specialization that maintains the insertion order of object keys - /// @sa https://json.nlohmann.me/api/ordered_json/ - using ordered_json = basic_json; - - NLOHMANN_JSON_NAMESPACE_END - -#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ - - -NLOHMANN_JSON_NAMESPACE_BEGIN -/*! -@brief detail namespace with internal helper functions - -This namespace collects functions that should not be exposed, -implementations of some @ref basic_json methods, and meta-programming helpers. - -@since version 2.1.0 -*/ -namespace detail -{ - -///////////// -// helpers // -///////////// - -// Note to maintainers: -// -// Every trait in this file expects a non CV-qualified type. -// The only exceptions are in the 'aliases for detected' section -// (i.e. those of the form: decltype(T::member_function(std::declval()))) -// -// In this case, T has to be properly CV-qualified to constraint the function arguments -// (e.g. to_json(BasicJsonType&, const T&)) - -template struct is_basic_json : std::false_type {}; - -NLOHMANN_BASIC_JSON_TPL_DECLARATION -struct is_basic_json : std::true_type {}; - -// used by exceptions create() member functions -// true_type for pointer to possibly cv-qualified basic_json or std::nullptr_t -// false_type otherwise -template -struct is_basic_json_context : - std::integral_constant < bool, - is_basic_json::type>::type>::value - || std::is_same::value > -{}; - -////////////////////// -// json_ref helpers // -////////////////////// - -template -class json_ref; - -template -struct is_json_ref : std::false_type {}; - -template -struct is_json_ref> : std::true_type {}; - -////////////////////////// -// aliases for detected // -////////////////////////// - -template -using mapped_type_t = typename T::mapped_type; - -template -using key_type_t = typename T::key_type; - -template -using value_type_t = typename T::value_type; - -template -using difference_type_t = typename T::difference_type; - -template -using pointer_t = typename T::pointer; - -template -using reference_t = typename T::reference; - -template -using iterator_category_t = typename T::iterator_category; - -template -using to_json_function = decltype(T::to_json(std::declval()...)); - -template -using from_json_function = decltype(T::from_json(std::declval()...)); - -template -using get_template_function = decltype(std::declval().template get()); - -// trait checking if JSONSerializer::from_json(json const&, udt&) exists -template -struct has_from_json : std::false_type {}; - -// trait checking if j.get is valid -// use this trait instead of std::is_constructible or std::is_convertible, -// both rely on, or make use of implicit conversions, and thus fail when T -// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) -template -struct is_getable -{ - static constexpr bool value = is_detected::value; -}; - -template -struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -// This trait checks if JSONSerializer::from_json(json const&) exists -// this overload is used for non-default-constructible user-defined-types -template -struct has_non_default_from_json : std::false_type {}; - -template -struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -// This trait checks if BasicJsonType::json_serializer::to_json exists -// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. -template -struct has_to_json : std::false_type {}; - -template -struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> -{ - using serializer = typename BasicJsonType::template json_serializer; - - static constexpr bool value = - is_detected_exact::value; -}; - -template -using detect_key_compare = typename T::key_compare; - -template -struct has_key_compare : std::integral_constant::value> {}; - -// obtains the actual object key comparator -template -struct actual_object_comparator -{ - using object_t = typename BasicJsonType::object_t; - using object_comparator_t = typename BasicJsonType::default_object_comparator_t; - using type = typename std::conditional < has_key_compare::value, - typename object_t::key_compare, object_comparator_t>::type; -}; - -template -using actual_object_comparator_t = typename actual_object_comparator::type; - -///////////////// -// char_traits // -///////////////// - -// Primary template of char_traits calls std char_traits -template -struct char_traits : std::char_traits -{}; - -// Explicitly define char traits for unsigned char since it is not standard -template<> -struct char_traits : std::char_traits -{ - using char_type = unsigned char; - using int_type = uint64_t; - - // Redefine to_int_type function - static int_type to_int_type(char_type c) noexcept - { - return static_cast(c); - } - - static char_type to_char_type(int_type i) noexcept - { - return static_cast(i); - } - - static constexpr int_type eof() noexcept - { - return static_cast(std::char_traits::eof()); - } -}; - -// Explicitly define char traits for signed char since it is not standard -template<> -struct char_traits : std::char_traits -{ - using char_type = signed char; - using int_type = uint64_t; - - // Redefine to_int_type function - static int_type to_int_type(char_type c) noexcept - { - return static_cast(c); - } - - static char_type to_char_type(int_type i) noexcept - { - return static_cast(i); - } - - static constexpr int_type eof() noexcept - { - return static_cast(std::char_traits::eof()); - } -}; - -/////////////////// -// is_ functions // -/////////////////// - -// https://en.cppreference.com/w/cpp/types/conjunction -template struct conjunction : std::true_type { }; -template struct conjunction : B { }; -template -struct conjunction -: std::conditional(B::value), conjunction, B>::type {}; - -// https://en.cppreference.com/w/cpp/types/negation -template struct negation : std::integral_constant < bool, !B::value > { }; - -// Reimplementation of is_constructible and is_default_constructible, due to them being broken for -// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). -// This causes compile errors in e.g. clang 3.5 or gcc 4.9. -template -struct is_default_constructible : std::is_default_constructible {}; - -template -struct is_default_constructible> - : conjunction, is_default_constructible> {}; - -template -struct is_default_constructible> - : conjunction, is_default_constructible> {}; - -template -struct is_default_constructible> - : conjunction...> {}; - -template -struct is_default_constructible> - : conjunction...> {}; - -template -struct is_constructible : std::is_constructible {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_constructible> : is_default_constructible> {}; - -template -struct is_iterator_traits : std::false_type {}; - -template -struct is_iterator_traits> -{ - private: - using traits = iterator_traits; - - public: - static constexpr auto value = - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value && - is_detected::value; -}; - -template -struct is_range -{ - private: - using t_ref = typename std::add_lvalue_reference::type; - - using iterator = detected_t; - using sentinel = detected_t; - - // to be 100% correct, it should use https://en.cppreference.com/w/cpp/iterator/input_or_output_iterator - // and https://en.cppreference.com/w/cpp/iterator/sentinel_for - // but reimplementing these would be too much work, as a lot of other concepts are used underneath - static constexpr auto is_iterator_begin = - is_iterator_traits>::value; - - public: - static constexpr bool value = !std::is_same::value && !std::is_same::value && is_iterator_begin; -}; - -template -using iterator_t = enable_if_t::value, result_of_begin())>>; - -template -using range_value_t = value_type_t>>; - -// The following implementation of is_complete_type is taken from -// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ -// and is written by Xiang Fan who agreed to using it in this library. - -template -struct is_complete_type : std::false_type {}; - -template -struct is_complete_type : std::true_type {}; - -template -struct is_compatible_object_type_impl : std::false_type {}; - -template -struct is_compatible_object_type_impl < - BasicJsonType, CompatibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> -{ - using object_t = typename BasicJsonType::object_t; - - // macOS's is_constructible does not play well with nonesuch... - static constexpr bool value = - is_constructible::value && - is_constructible::value; -}; - -template -struct is_compatible_object_type - : is_compatible_object_type_impl {}; - -template -struct is_constructible_object_type_impl : std::false_type {}; - -template -struct is_constructible_object_type_impl < - BasicJsonType, ConstructibleObjectType, - enable_if_t < is_detected::value&& - is_detected::value >> -{ - using object_t = typename BasicJsonType::object_t; - - static constexpr bool value = - (is_default_constructible::value && - (std::is_move_assignable::value || - std::is_copy_assignable::value) && - (is_constructible::value && - std::is_same < - typename object_t::mapped_type, - typename ConstructibleObjectType::mapped_type >::value)) || - (has_from_json::value || - has_non_default_from_json < - BasicJsonType, - typename ConstructibleObjectType::mapped_type >::value); -}; - -template -struct is_constructible_object_type - : is_constructible_object_type_impl {}; - -template -struct is_compatible_string_type -{ - static constexpr auto value = - is_constructible::value; -}; - -template -struct is_constructible_string_type -{ - // launder type through decltype() to fix compilation failure on ICPC -#ifdef __INTEL_COMPILER - using laundered_type = decltype(std::declval()); -#else - using laundered_type = ConstructibleStringType; -#endif - - static constexpr auto value = - conjunction < - is_constructible, - is_detected_exact>::value; -}; - -template -struct is_compatible_array_type_impl : std::false_type {}; - -template -struct is_compatible_array_type_impl < - BasicJsonType, CompatibleArrayType, - enable_if_t < - is_detected::value&& - is_iterator_traits>>::value&& -// special case for types like std::filesystem::path whose iterator's value_type are themselves -// c.f. https://github.com/nlohmann/json/pull/3073 - !std::is_same>::value >> -{ - static constexpr bool value = - is_constructible>::value; -}; - -template -struct is_compatible_array_type - : is_compatible_array_type_impl {}; - -template -struct is_constructible_array_type_impl : std::false_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t::value >> - : std::true_type {}; - -template -struct is_constructible_array_type_impl < - BasicJsonType, ConstructibleArrayType, - enable_if_t < !std::is_same::value&& - !is_compatible_string_type::value&& - is_default_constructible::value&& -(std::is_move_assignable::value || - std::is_copy_assignable::value)&& -is_detected::value&& -is_iterator_traits>>::value&& -is_detected::value&& -// special case for types like std::filesystem::path whose iterator's value_type are themselves -// c.f. https://github.com/nlohmann/json/pull/3073 -!std::is_same>::value&& -is_complete_type < -detected_t>::value >> -{ - using value_type = range_value_t; - - static constexpr bool value = - std::is_same::value || - has_from_json::value || - has_non_default_from_json < - BasicJsonType, - value_type >::value; -}; - -template -struct is_constructible_array_type - : is_constructible_array_type_impl {}; - -template -struct is_compatible_integer_type_impl : std::false_type {}; - -template -struct is_compatible_integer_type_impl < - RealIntegerType, CompatibleNumberIntegerType, - enable_if_t < std::is_integral::value&& - std::is_integral::value&& - !std::is_same::value >> -{ - // is there an assert somewhere on overflows? - using RealLimits = std::numeric_limits; - using CompatibleLimits = std::numeric_limits; - - static constexpr auto value = - is_constructible::value && - CompatibleLimits::is_integer && - RealLimits::is_signed == CompatibleLimits::is_signed; -}; - -template -struct is_compatible_integer_type - : is_compatible_integer_type_impl {}; - -template -struct is_compatible_type_impl: std::false_type {}; - -template -struct is_compatible_type_impl < - BasicJsonType, CompatibleType, - enable_if_t::value >> -{ - static constexpr bool value = - has_to_json::value; -}; - -template -struct is_compatible_type - : is_compatible_type_impl {}; - -template -struct is_constructible_tuple : std::false_type {}; - -template -struct is_constructible_tuple> : conjunction...> {}; - -template -struct is_json_iterator_of : std::false_type {}; - -template -struct is_json_iterator_of : std::true_type {}; - -template -struct is_json_iterator_of : std::true_type -{}; - -// checks if a given type T is a template specialization of Primary -template