mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
Compare commits
34 Commits
v0.1.4
...
a54858e4c6
| Author | SHA1 | Date | |
|---|---|---|---|
| a54858e4c6 | |||
| 18e308bd03 | |||
| 2e4a32fed7 | |||
| c5ce91e653 | |||
| 412f88565a | |||
| 8451921161 | |||
| d6f304e6ca | |||
| 826c6600d0 | |||
| b166bab4f3 | |||
| efa49a98b7 | |||
| 3a9ab6a14a | |||
| d15037e7a3 | |||
| 0ff5820e6e | |||
| 5e96c1573b | |||
| 90273e87f2 | |||
| 1e106d75a3 | |||
| bd1a9557a5 | |||
| 831569cec6 | |||
| a5c1c7d4aa | |||
| 802d2ecb5a | |||
| ab605f7590 | |||
| 15d5af34b9 | |||
| 6d0e60e241 | |||
| 7b530968af | |||
| 930226cc47 | |||
| f9bf2f4b89 | |||
| df3ac5b5db | |||
| 2568eb7d18 | |||
| 2e3b4f4f0f | |||
| 5951a5e81c | |||
| 849386a3bc | |||
| 3e7ce71219 | |||
| 94fed9a810 | |||
| f4174724c6 |
@@ -148,6 +148,7 @@ target_link_libraries(${PROJECT_NAME}
|
||||
OpenAL::OpenAL
|
||||
harfbuzz::harfbuzz
|
||||
Opus::Opus
|
||||
assimp::assimp
|
||||
$<$<PLATFORM_ID:Windows>:ws2_32>
|
||||
|
||||
)
|
||||
|
||||
16
assets/model/creature/pig/collision.json
Normal file
16
assets/model/creature/pig/collision.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"boxes": [
|
||||
{
|
||||
"center": [
|
||||
0,
|
||||
0.78,
|
||||
0
|
||||
],
|
||||
"half": [
|
||||
0.53,
|
||||
0.78,
|
||||
0.406
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/model/creature/pig/pig.glb
Normal file
BIN
assets/model/creature/pig/pig.glb
Normal file
Binary file not shown.
BIN
assets/model/creature/player/player.glb
Normal file
BIN
assets/model/creature/player/player.glb
Normal file
Binary file not shown.
@@ -7,7 +7,6 @@ uniform mat4 lightSpaceMatrix;
|
||||
uniform mat4 modelMatrix;
|
||||
|
||||
out vec2 tc;
|
||||
flat out int tex_layer;
|
||||
|
||||
void main() {
|
||||
tc = texCoord;
|
||||
@@ -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;
|
||||
@@ -10,7 +10,7 @@ find_package(harfbuzz REQUIRED)
|
||||
find_package(Freetype REQUIRED)
|
||||
find_package(SDL3 REQUIRED)
|
||||
find_package(Opus REQUIRED)
|
||||
|
||||
find_package(assimp REQUIRED)
|
||||
# Third-party libraries
|
||||
|
||||
if (WIN32)
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
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
|
||||
@@ -49,7 +49,6 @@ private:
|
||||
Window m_window;
|
||||
|
||||
TextureManager m_texture_manager;
|
||||
|
||||
AudioEngine m_audio;
|
||||
|
||||
Renderer m_renderer;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
27
include/Cubed/gameplay/chunk.hpp
Normal file
27
include/Cubed/gameplay/chunk.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/chunk_pos.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <tuple>
|
||||
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<int, int, int> world_to_block(int world_x, int world_y,
|
||||
int world_z, int chunk_x,
|
||||
int chunk_z);
|
||||
static std::tuple<int, int, int> world_to_block(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
static std::tuple<int, int, int> block_to_world(int x, int y, int z,
|
||||
int chunk_x, int chunk_z);
|
||||
static std::tuple<int, int, int> block_to_world(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -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<int, int, int> world_to_block(int world_x, int world_y,
|
||||
int world_z, int chunk_x,
|
||||
int chunk_z);
|
||||
static std::tuple<int, int, int> world_to_block(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
static std::tuple<int, int, int> block_to_world(int x, int y, int z,
|
||||
int chunk_x, int chunk_z);
|
||||
static std::tuple<int, int, int> block_to_world(const glm::ivec3& block_pos,
|
||||
ChunkPos chunk_pos);
|
||||
BiomeType get_biome() const;
|
||||
ChunkPos get_chunk_pos() const;
|
||||
const std::vector<BlockType>& get_chunk_blocks() const;
|
||||
|
||||
76
include/Cubed/gameplay/client_entity_manager.hpp
Normal file
76
include/Cubed/gameplay/client_entity_manager.hpp
Normal file
@@ -0,0 +1,76 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/entity.hpp"
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
#include "world/entity.pb.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
namespace Cubed {
|
||||
class ClientWorld;
|
||||
class ClientEntityManager {
|
||||
public:
|
||||
enum class Command { CREATE, DESTORY, UPDATE };
|
||||
|
||||
ClientEntityManager(ClientWorld& world);
|
||||
void update();
|
||||
void init();
|
||||
|
||||
void receive_entity_create(S2CEntityCreate& msg);
|
||||
void receive_entity_destory(EntityID id);
|
||||
void receive_entity_update(S2CEntityUpdate& msg);
|
||||
|
||||
void destory(EntityID id);
|
||||
void create(std::string_view name, const glm::vec3& pos);
|
||||
|
||||
const entt::registry& get_registry() const;
|
||||
|
||||
private:
|
||||
struct EntityCreateElement {
|
||||
EntityID id;
|
||||
std::string name;
|
||||
glm::vec3 pos;
|
||||
};
|
||||
|
||||
struct UpdateInfo {
|
||||
EntityID id;
|
||||
glm::vec3 pos;
|
||||
};
|
||||
|
||||
using EntityMap = tbb::concurrent_hash_map<EntityID, entt::entity>;
|
||||
using acc = EntityMap::accessor;
|
||||
using cacc = EntityMap::const_accessor;
|
||||
using CreateFunc = std::function<void(EntityID id)>;
|
||||
using TaskElement = std::variant<EntityCreateElement, EntityID, UpdateInfo>;
|
||||
using TaskPair = std::pair<Command, TaskElement>;
|
||||
|
||||
ClientWorld& m_world;
|
||||
entt::registry m_registry;
|
||||
EntityMap m_entities;
|
||||
std::unordered_map<std::string_view, CreateFunc> m_factories;
|
||||
tbb::concurrent_queue<TaskPair> m_tasks;
|
||||
|
||||
void handle_task();
|
||||
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);
|
||||
template <typename... Args>
|
||||
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<std::remove_cvref_t<Args>>(
|
||||
entity, std::forward<Args>(args))),
|
||||
...);
|
||||
m_entities.emplace(id, entity);
|
||||
return;
|
||||
}
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -1,167 +1,25 @@
|
||||
#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 <absl/container/flat_hash_set.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
#include "Cubed/gameplay/ecs/animation.hpp"
|
||||
#include "Cubed/gameplay/ecs/identity.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/player.hpp"
|
||||
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<ChunkPos, ChunkPos::Hash>;
|
||||
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<LookBlock>& 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<const ItemStack, HOTBAR_SUM> 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<float> m_yaw = 0.0f;
|
||||
std::atomic<float> m_pitch = 0.0f;
|
||||
std::array<ItemStack, HOTBAR_SUM> 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<Gait> m_gait = Gait::STOP;
|
||||
MoveState m_move_state{};
|
||||
MouseState m_mouse_state{};
|
||||
GameMode m_game_mode = CREATIVE;
|
||||
std::optional<LookBlock> 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<std::string, Timer> 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 ClientPlayer {
|
||||
Position pos{};
|
||||
Position render_pos{};
|
||||
EntityInfo entity{};
|
||||
WalkPose walk{};
|
||||
Orientation angle{};
|
||||
Orientation render_angle{};
|
||||
};
|
||||
|
||||
struct PlayerRenderData {
|
||||
EntityInfo info{};
|
||||
Position render_pos{};
|
||||
Orientation angle{};
|
||||
Gait gait{};
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
45
include/Cubed/gameplay/client_player_manager.hpp
Normal file
45
include/Cubed/gameplay/client_player_manager.hpp
Normal file
@@ -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<PlayerRenderData> 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<ClientPlayer>::Handle;
|
||||
SparseVector<ClientPlayer> m_players;
|
||||
std::vector<PlayerRenderData> m_render_data;
|
||||
std::unordered_map<std::string, PlayerHandle> m_players_handle;
|
||||
LocalPlayer m_local;
|
||||
|
||||
void update_players_data(float dt);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -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 <tbb/concurrent_unordered_map.h>
|
||||
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<NetworkClient> client);
|
||||
void update(float delta_time);
|
||||
void update(float dt);
|
||||
bool handle_event(const Event& e);
|
||||
const std::optional<LookBlock>& 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<glm::vec4>& planes();
|
||||
const std::vector<const ChunkRenderSnapshot*>& render_snapshots() const;
|
||||
const std::vector<PlayerRenderData>& render_player_data() const;
|
||||
std::vector<PlayerRenderData>& render_player_data();
|
||||
|
||||
glm::vec3 sunlight_dir() const;
|
||||
bool sphere_collide_world(glm::vec3 center, float radius) const;
|
||||
@@ -99,22 +79,25 @@ 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<NetworkClient> 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;
|
||||
template <typename Fn>
|
||||
void register_ticktimer(std::string_view id, TickType threshold, Fn&& f) {
|
||||
m_ticktimers.emplace(
|
||||
std::piecewise_construct, std::forward_as_tuple(std::string(id)),
|
||||
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<Fn>(f)));
|
||||
}
|
||||
|
||||
@@ -136,7 +119,6 @@ private:
|
||||
ChunkPos::TBBHash>;
|
||||
using ChunkPosSet = absl::flat_hash_set<ChunkPos, ChunkPos::Hash>;
|
||||
using ChunkPosVector = std::vector<ChunkPos>;
|
||||
using OtherPlayerHashMap = std::unordered_map<std::string, PlayerInfo>;
|
||||
using chunk_acc = ChunkHashMap::accessor;
|
||||
using chunk_cacc = ChunkHashMap::const_accessor;
|
||||
|
||||
@@ -147,16 +129,13 @@ private:
|
||||
|
||||
static constexpr int WORLD_EXIT_TIMEOUT = 200;
|
||||
static constexpr int MAX_UPLOAD_CHUNK_SUM = 16;
|
||||
ClientPlayer m_player;
|
||||
OtherPlayerHashMap m_player_info;
|
||||
ClientEntityManager m_entity_manager;
|
||||
ClientPlayerManager m_player_manager;
|
||||
ChunkHashMap m_chunks;
|
||||
AudioEngine& m_audio;
|
||||
Config& m_config;
|
||||
WorldScene& m_world_scene;
|
||||
std::vector<glm::vec4> m_planes;
|
||||
std::jthread m_client_thread;
|
||||
|
||||
mutable std::shared_mutex m_player_info_mutex;
|
||||
|
||||
tbb::concurrent_queue<std::unique_ptr<ClientChunk>> m_pending_upload_queue;
|
||||
tbb::concurrent_queue<ChunkPos> m_dirty_chunk_queue;
|
||||
@@ -166,9 +145,7 @@ private:
|
||||
|
||||
std::deque<ChunkPos> m_dirty_queue;
|
||||
std::vector<const ChunkRenderSnapshot*> m_render_snapshots;
|
||||
std::vector<PlayerRenderData> m_render_player_data;
|
||||
|
||||
tbb::concurrent_unordered_map<std::string, TickTimer> m_ticktimers;
|
||||
std::unordered_map<std::string, Timer> m_timers;
|
||||
std::atomic<bool> m_exit_direct{false};
|
||||
std::atomic<bool> m_game_running{false};
|
||||
@@ -176,6 +153,7 @@ private:
|
||||
std::atomic<int> m_rendering_distance{24};
|
||||
std::atomic<TickType> m_game_ticks{0};
|
||||
std::atomic<TickType> m_day_tick{6000};
|
||||
std::atomic<int> m_per_tick_time = DEFAULT_PER_TICK_TIME;
|
||||
std::atomic<bool> m_requesting_chunk{false};
|
||||
std::atomic<bool> m_is_rebuilding{false};
|
||||
std::atomic<int> m_chunk_task_id{0};
|
||||
@@ -187,10 +165,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);
|
||||
|
||||
6
include/Cubed/gameplay/creatures/pig.hpp
Normal file
6
include/Cubed/gameplay/creatures/pig.hpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace Cubed {
|
||||
struct PigTag {};
|
||||
|
||||
} // namespace Cubed
|
||||
17
include/Cubed/gameplay/ecs/ai_struct.hpp
Normal file
17
include/Cubed/gameplay/ecs/ai_struct.hpp
Normal file
@@ -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
|
||||
13
include/Cubed/gameplay/ecs/animation.hpp
Normal file
13
include/Cubed/gameplay/ecs/animation.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/player.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
|
||||
14
include/Cubed/gameplay/ecs/client_entity.hpp
Normal file
14
include/Cubed/gameplay/ecs/client_entity.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/animation.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/model.hpp"
|
||||
namespace Cubed {
|
||||
struct BaseClientCreature {
|
||||
|
||||
Transform transform;
|
||||
|
||||
WalkPose pose;
|
||||
|
||||
ModelID model;
|
||||
};
|
||||
} // namespace Cubed
|
||||
11
include/Cubed/gameplay/ecs/entity.hpp
Normal file
11
include/Cubed/gameplay/ecs/entity.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
namespace Cubed {
|
||||
using EntityID = uint64_t;
|
||||
|
||||
struct Entity {
|
||||
EntityID id;
|
||||
explicit Entity(EntityID id) : id(id) {}
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
10
include/Cubed/gameplay/ecs/health.hpp
Normal file
10
include/Cubed/gameplay/ecs/health.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
struct Health {
|
||||
float hp = 20;
|
||||
float max_hp = 20;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
10
include/Cubed/gameplay/ecs/identity.hpp
Normal file
10
include/Cubed/gameplay/ecs/identity.hpp
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace Cubed {
|
||||
struct EntityInfo {
|
||||
std::string name;
|
||||
std::string uuid;
|
||||
};
|
||||
} // namespace Cubed
|
||||
34
include/Cubed/gameplay/ecs/movement.hpp
Normal file
34
include/Cubed/gameplay/ecs/movement.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "Cubed/constants.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
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
|
||||
24
include/Cubed/gameplay/ecs/server_entity.hpp
Normal file
24
include/Cubed/gameplay/ecs/server_entity.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#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{};
|
||||
|
||||
Direction direction{};
|
||||
|
||||
Gravity gravity{};
|
||||
|
||||
Health health{};
|
||||
|
||||
HitboxID hitbox{};
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
18
include/Cubed/gameplay/ecs/state.hpp
Normal file
18
include/Cubed/gameplay/ecs/state.hpp
Normal file
@@ -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
|
||||
24
include/Cubed/gameplay/ecs/transform.hpp
Normal file
24
include/Cubed/gameplay/ecs/transform.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
#include <glm/glm.hpp>
|
||||
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{};
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
26
include/Cubed/gameplay/hitbox.hpp
Normal file
26
include/Cubed/gameplay/hitbox.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
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
|
||||
37
include/Cubed/gameplay/hitbox_manager.hpp
Normal file
37
include/Cubed/gameplay/hitbox_manager.hpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/hitbox.hpp"
|
||||
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
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<HitboxID, Hitbox>;
|
||||
using IDMap = tbb::concurrent_hash_map<std::string, HitboxID>;
|
||||
using NameMap = tbb::concurrent_hash_map<HitboxID, std::string>;
|
||||
HitboxID m_next = 0;
|
||||
IDMap m_id_map;
|
||||
NameMap m_name_map;
|
||||
HitboxMap m_hitboxes;
|
||||
Handle load(std::string_view name);
|
||||
};
|
||||
} // namespace Cubed
|
||||
162
include/Cubed/gameplay/local_player.hpp
Normal file
162
include/Cubed/gameplay/local_player.hpp
Normal file
@@ -0,0 +1,162 @@
|
||||
#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/gameplay/player.hpp"
|
||||
#include "Cubed/input/event.hpp"
|
||||
#include "Cubed/input/input.hpp"
|
||||
|
||||
#include <absl/container/flat_hash_set.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <optional>
|
||||
#include <shared_mutex>
|
||||
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<ChunkPos, ChunkPos::Hash>;
|
||||
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<LookBlock>& 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<const ItemStack, HOTBAR_SUM> 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<ItemStack, HOTBAR_SUM> 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<LookBlock> 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<std::string, Timer> 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<bool, bool, bool> update_physical(float dt, glm::vec3& pos);
|
||||
glm::vec3 get_move_distance(float dt);
|
||||
};
|
||||
} // namespace Cubed
|
||||
7
include/Cubed/gameplay/model.hpp
Normal file
7
include/Cubed/gameplay/model.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
using ModelID = uint32_t;
|
||||
struct Model {
|
||||
ModelID id;
|
||||
};
|
||||
@@ -51,18 +51,27 @@ 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,
|
||||
|
||||
CHAT_MSG = 4001,
|
||||
VOICE_MSG = 4002,
|
||||
|
||||
PING = 9001,
|
||||
PONG = 9002
|
||||
|
||||
@@ -111,6 +120,18 @@ template <> constexpr uint16_t get_packet_id<BlockChangeRsp>() {
|
||||
template <> constexpr uint16_t get_packet_id<S2C_ClearAllChunks>() {
|
||||
return std::to_underlying(PacketEnum::S2C_CLEAR_ALL_CHUNKS);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<S2CEntityCreate>() {
|
||||
return std::to_underlying(PacketEnum::S2C_ENTITY_CREATE);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<S2CEntityDestory>() {
|
||||
return std::to_underlying(PacketEnum::S2C_ENTITY_DESTORY);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<C2SEntityCreateRequest>() {
|
||||
return std::to_underlying(PacketEnum::C2S_ENTITY_CREATE_REQUEST);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<C2SEntityDestoryRequest>() {
|
||||
return std::to_underlying(PacketEnum::C2S_ENTITY_DESTORY_REQUEST);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<UpdateTime>() {
|
||||
return std::to_underlying(PacketEnum::UPDATE_TIME);
|
||||
}
|
||||
@@ -129,6 +150,9 @@ template <> constexpr uint16_t get_packet_id<ChatMsg>() {
|
||||
template <> constexpr uint16_t get_packet_id<VoiceMsg>() {
|
||||
return std::to_underlying(PacketEnum::VOICE_MSG);
|
||||
}
|
||||
template <> constexpr uint16_t get_packet_id<S2CEntityUpdate>() {
|
||||
return std::to_underlying(PacketEnum::S2C_ENTITY_UPDATE);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::derived_from<T, google::protobuf::Message>
|
||||
@@ -180,6 +204,12 @@ Packet make_packet(const T& msg) {
|
||||
return packet;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
requires std::derived_from<T, google::protobuf::Message>
|
||||
Packet make_packet(const T* msg) {
|
||||
return make_packet(*msg);
|
||||
}
|
||||
|
||||
inline PacketHeader decode_packet_header(std::span<const uint8_t> header) {
|
||||
if (header.size() < HEADER_LEN)
|
||||
throw std::runtime_error("Invalid header");
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
namespace Cubed {
|
||||
@@ -17,5 +19,5 @@ inline Gait get_gait_from_id(int id) {
|
||||
throw std::runtime_error("Unknown Gait");
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr glm::vec3 PLAYER_SIZE{0.6f, 1.8f, 0.6f};
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -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 <tuple>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class ServerChunk {
|
||||
class ServerChunk : public Chunk {
|
||||
public:
|
||||
ServerChunk(ServerWorld& world, ChunkPos chunk_pos,
|
||||
bool temp_chunk = false);
|
||||
|
||||
62
include/Cubed/gameplay/server_entity_manager.hpp
Normal file
62
include/Cubed/gameplay/server_entity_manager.hpp
Normal file
@@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/entity.hpp"
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
#include <tbb/concurrent_queue.h>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class Session;
|
||||
class ServerEntityManager {
|
||||
public:
|
||||
ServerEntityManager(ServerWorld& world);
|
||||
|
||||
void init();
|
||||
void update();
|
||||
// not thread safe
|
||||
void add_entity(std::string_view name, const glm::vec3& pos);
|
||||
void destory(EntityID id);
|
||||
void handle_player_login(std::shared_ptr<Session> session);
|
||||
|
||||
private:
|
||||
enum class Command { CREATE, SEND_ALL_ENTITIES, DESTORY };
|
||||
struct EntityCreateElement {
|
||||
std::string name;
|
||||
glm::vec3 pos;
|
||||
};
|
||||
using EntityMap = tbb::concurrent_hash_map<EntityID, entt::entity>;
|
||||
using acc = EntityMap::accessor;
|
||||
using cacc = EntityMap::const_accessor;
|
||||
using CreateFunc = std::function<EntityID()>;
|
||||
using TaskElement =
|
||||
std::variant<std::shared_ptr<Session>, EntityCreateElement, EntityID>;
|
||||
using TaskPair = std::pair<Command, TaskElement>;
|
||||
ServerWorld& m_world;
|
||||
tbb::concurrent_queue<TaskPair> m_tasks;
|
||||
entt::registry m_registry;
|
||||
EntityID m_next = 0;
|
||||
EntityMap m_entities;
|
||||
std::unordered_map<std::string_view, CreateFunc> 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>& session);
|
||||
void update_ai();
|
||||
void update_move();
|
||||
void update_send();
|
||||
template <typename... Args>
|
||||
EntityID create_entity_in_factory(Args&&... args) {
|
||||
auto entity = m_registry.create();
|
||||
|
||||
((m_registry.emplace<std::remove_cvref_t<Args>>(
|
||||
entity, std::forward<Args>(args))),
|
||||
...);
|
||||
auto id = m_next++;
|
||||
m_entities.emplace(id, entity);
|
||||
return id;
|
||||
}
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -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"
|
||||
@@ -25,7 +27,7 @@
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
class Session;
|
||||
class ServerWorld {
|
||||
class ServerWorld : public World {
|
||||
public:
|
||||
enum class ThreadPoolKind { NET, GEN };
|
||||
ServerWorld(Config& config);
|
||||
@@ -84,7 +86,19 @@ 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;
|
||||
|
||||
std::vector<std::shared_ptr<Session>> get_all_session() 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 <typename Fn>
|
||||
void register_timer(std::string_view id, TickType threshold, Fn&& f) {
|
||||
m_timers.emplace(std::piecewise_construct,
|
||||
@@ -119,13 +133,13 @@ private:
|
||||
using PlayerUUIDMap = tbb::concurrent_hash_map<std::string, std::string>;
|
||||
|
||||
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;
|
||||
|
||||
ServerEntityManager m_entity_manager;
|
||||
// key = uuid
|
||||
PlayerHashMap m_players;
|
||||
ChunkHashMap m_chunks;
|
||||
@@ -155,7 +169,7 @@ private:
|
||||
std::atomic<bool> m_tick_running{true};
|
||||
std::atomic<int> 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;
|
||||
|
||||
|
||||
17
include/Cubed/gameplay/systems/physical_system.hpp
Normal file
17
include/Cubed/gameplay/systems/physical_system.hpp
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/movement.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class PhysicalSystem {
|
||||
public:
|
||||
static glm::vec3 get_move_distance(const Direction& d,
|
||||
const TickVelocity& v);
|
||||
|
||||
static void update(ServerWorld& world, entt::registry& registry);
|
||||
|
||||
private:
|
||||
};
|
||||
} // namespace Cubed
|
||||
11
include/Cubed/gameplay/systems/speed_system.hpp
Normal file
11
include/Cubed/gameplay/systems/speed_system.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
namespace Cubed {
|
||||
class SpeedSystem {
|
||||
public:
|
||||
static void update(float dt, entt::registry& registry);
|
||||
|
||||
private:
|
||||
};
|
||||
} // namespace Cubed
|
||||
14
include/Cubed/gameplay/systems/wander_ai_system.hpp
Normal file
14
include/Cubed/gameplay/systems/wander_ai_system.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/ai_struct.hpp"
|
||||
#include "Cubed/gameplay/ecs/server_entity.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
namespace Cubed {
|
||||
class WanderAISystem {
|
||||
public:
|
||||
static void update(entt::registry& registry);
|
||||
|
||||
private:
|
||||
static void do_ai(BaseServerCreature& creature, MoveBoost& move_boost);
|
||||
};
|
||||
} // namespace Cubed
|
||||
29
include/Cubed/gameplay/world.hpp
Normal file
29
include/Cubed/gameplay/world.hpp
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/block.hpp"
|
||||
#include "Cubed/gameplay/hitbox.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
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<float>(pos.x) + 0.5f,
|
||||
static_cast<float>(pos.y) + 0.5f,
|
||||
static_cast<float>(pos.z) + 0.5f},
|
||||
glm::vec3{0.5f, 0.5f, 0.5f}};
|
||||
}
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -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;
|
||||
|
||||
45
include/Cubed/render/model_manager.hpp
Normal file
45
include/Cubed/render/model_manager.hpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/model.hpp"
|
||||
#include "Cubed/render/model_node.hpp"
|
||||
#include "Cubed/tools/model_loader.hpp"
|
||||
|
||||
#include <tbb/concurrent_hash_map.h>
|
||||
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<ModelID, ModelNode>;
|
||||
using IDMap = tbb::concurrent_hash_map<std::string, ModelID>;
|
||||
using NameMap = tbb::concurrent_hash_map<ModelID, std::string>;
|
||||
ModelMap m_models;
|
||||
IDMap m_id_map;
|
||||
NameMap m_name_map;
|
||||
Handle load_model(std::string_view model_name);
|
||||
};
|
||||
} // namespace Cubed
|
||||
42
include/Cubed/render/model_node.hpp
Normal file
42
include/Cubed/render/model_node.hpp
Normal file
@@ -0,0 +1,42 @@
|
||||
#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 <glad/glad.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
namespace Cubed {
|
||||
|
||||
struct Mesh {
|
||||
std::vector<Vertex3D> vertices;
|
||||
std::vector<uint32_t> indices;
|
||||
std::unique_ptr<VertexBuffer> vbo;
|
||||
std::unique_ptr<VertexBuffer> ebo;
|
||||
std::unique_ptr<VertexArray> vao;
|
||||
std::unique_ptr<Texture> texture;
|
||||
void upload() {
|
||||
vao = std::make_unique<VertexArray>();
|
||||
vao->bind();
|
||||
vbo = std::make_unique<VertexBuffer>();
|
||||
vbo->buffer_data(vertices.data(), vertices.size() * sizeof(Vertex3D));
|
||||
ebo = std::make_unique<VertexBuffer>(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 ModelNode {
|
||||
std::string name;
|
||||
glm::mat4 transform{1.0f};
|
||||
std::vector<Mesh> meshes;
|
||||
std::vector<ModelNode> children;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
24
include/Cubed/render/model_renderer.hpp
Normal file
24
include/Cubed/render/model_renderer.hpp
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/model.hpp"
|
||||
#include "Cubed/render/model_node.hpp"
|
||||
#include "Cubed/shader.hpp"
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
namespace Cubed {
|
||||
class Renderer;
|
||||
class Camera;
|
||||
class ModelRender {
|
||||
public:
|
||||
ModelRender(Renderer& renderer);
|
||||
void render_model(ModelID id, const glm::vec3& pos, Camera& camera);
|
||||
|
||||
void shadow_pass(ModelID id, const glm::vec3& pos, Camera& camera);
|
||||
|
||||
private:
|
||||
Renderer& m_renderer;
|
||||
void render_node(const ModelNode& node, const glm::mat4& parent,
|
||||
const glm::mat4& view, const Shader& shader, bool shadow);
|
||||
void render_mesh(const Mesh& mesh, bool shadow);
|
||||
};
|
||||
} // namespace Cubed
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<Vertex2D> m_ui;
|
||||
|
||||
WorldRenderer m_world_renderer;
|
||||
ModelRender m_model_renderer;
|
||||
Config& m_config;
|
||||
|
||||
bool handle_window_resize_event(const WindowResizeEvent& e);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -132,7 +132,8 @@ private:
|
||||
|
||||
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);
|
||||
void render_entity(ClientWorld& world);
|
||||
|
||||
void render_normal_block(const glm::mat4& model_mat,
|
||||
const glm::mat4& mv_mat, const glm::mat4& norm_mat,
|
||||
|
||||
7
include/Cubed/tools/cubed_concepts.hpp
Normal file
7
include/Cubed/tools/cubed_concepts.hpp
Normal file
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
namespace Cubed {
|
||||
template <typename T>
|
||||
concept Ptr = std::is_pointer_v<T>;
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
#pragma once
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
|
||||
#include <random>
|
||||
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;
|
||||
|
||||
22
include/Cubed/tools/model_loader.hpp
Normal file
22
include/Cubed/tools/model_loader.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include "Cubed/render/model_node.hpp"
|
||||
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
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
|
||||
19
include/Cubed/tools/name_space.hpp
Normal file
19
include/Cubed/tools/name_space.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
inline std::vector<std::string_view> parse_namespace(std::string_view str) {
|
||||
std::vector<std::string_view> 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
|
||||
21
include/Cubed/tools/net_utils.hpp
Normal file
21
include/Cubed/tools/net_utils.hpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/tools/cubed_concepts.hpp"
|
||||
#include "common/vector3.pb.h"
|
||||
#include "glm/ext/vector_float3.hpp"
|
||||
namespace Cubed {
|
||||
namespace Tools {
|
||||
template <Ptr T> void set_net_pos(T ptr, const glm::vec3& pos) {
|
||||
Vec3* p = ptr->mutable_pos();
|
||||
p->set_x(pos.x);
|
||||
p->set_y(pos.y);
|
||||
p->set_z(pos.z);
|
||||
}
|
||||
inline glm::vec3 get_net_pos(const Vec3* p) {
|
||||
return glm::vec3{p->x(), p->y(), p->z()};
|
||||
}
|
||||
inline glm::vec3 get_net_pos(const Vec3& p) {
|
||||
return glm::vec3{p.x(), p.y(), p.z()};
|
||||
}
|
||||
} // namespace Tools
|
||||
} // namespace Cubed
|
||||
@@ -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
|
||||
|
||||
|
||||
318
include/Cubed/tools/sparse_vector.hpp
Normal file
318
include/Cubed/tools/sparse_vector.hpp
Normal file
@@ -0,0 +1,318 @@
|
||||
#pragma once
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iterator>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
namespace Cubed {
|
||||
template <typename T> class SparseVector {
|
||||
private:
|
||||
std::vector<std::optional<T>> m_data;
|
||||
std::vector<uint32_t> m_free_list;
|
||||
std::vector<uint32_t> m_generation;
|
||||
|
||||
std::vector<uint32_t> m_dense;
|
||||
std::vector<uint32_t> 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<difference_type>(m_index) -
|
||||
static_cast<difference_type>(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<difference_type>(m_index) -
|
||||
static_cast<difference_type>(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 <class U> [[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<U>(value));
|
||||
|
||||
m_dense_index[id] = m_dense.size();
|
||||
} else {
|
||||
id = m_data.size();
|
||||
m_data.push_back(std::forward<U>(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 <typename... Args> Handle emplace(Args&&... args) {
|
||||
return insert(T(std::forward<Args>(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
|
||||
2
include/entt/.clang-format
Normal file
2
include/entt/.clang-format
Normal file
@@ -0,0 +1,2 @@
|
||||
DisableFormat: true
|
||||
SortIncludes: false
|
||||
134
include/entt/config/config.h
Normal file
134
include/entt/config/config.h
Normal file
@@ -0,0 +1,134 @@
|
||||
#ifndef ENTT_CONFIG_CONFIG_H
|
||||
#define ENTT_CONFIG_CONFIG_H
|
||||
|
||||
#if __has_include(<entt/ext/config.h>)
|
||||
# include <entt/ext/config.h>
|
||||
#endif
|
||||
|
||||
#include <version>
|
||||
#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<Type>
|
||||
#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 <cassert>
|
||||
# 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
|
||||
11
include/entt/config/macro.h
Normal file
11
include/entt/config/macro.h
Normal file
@@ -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
|
||||
18
include/entt/config/version.h
Normal file
18
include/entt/config/version.h
Normal file
@@ -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
|
||||
1026
include/entt/container/dense_map.hpp
Normal file
1026
include/entt/container/dense_map.hpp
Normal file
File diff suppressed because it is too large
Load Diff
890
include/entt/container/dense_set.hpp
Normal file
890
include/entt/container/dense_set.hpp
Normal file
@@ -0,0 +1,890 @@
|
||||
#ifndef ENTT_CONTAINER_DENSE_SET_HPP
|
||||
#define ENTT_CONTAINER_DENSE_SET_HPP
|
||||
|
||||
#include <compare>
|
||||
#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<stl::size_t>::max)();
|
||||
|
||||
template<typename It>
|
||||
class dense_set_iterator final {
|
||||
template<typename>
|
||||
friend class dense_set_iterator;
|
||||
|
||||
static_assert(stl::is_pointer_v<It>, "Not a pointer type");
|
||||
|
||||
public:
|
||||
using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::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<typename Other>
|
||||
requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
|
||||
constexpr dense_set_iterator(const dense_set_iterator<Other> &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<typename Other>
|
||||
[[nodiscard]] constexpr stl::ptrdiff_t operator-(const dense_set_iterator<Other> &other) const noexcept {
|
||||
return it - other.it;
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_set_iterator<Other> &other) const noexcept {
|
||||
return it == other.it;
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] constexpr auto operator<=>(const dense_set_iterator<Other> &other) const noexcept {
|
||||
return it <=> other.it;
|
||||
}
|
||||
|
||||
private:
|
||||
It it;
|
||||
};
|
||||
|
||||
template<typename It>
|
||||
class dense_set_local_iterator final {
|
||||
template<typename>
|
||||
friend class dense_set_local_iterator;
|
||||
|
||||
static_assert(stl::is_pointer_v<It>, "Not a pointer type");
|
||||
|
||||
public:
|
||||
using value_type = stl::remove_const_t<stl::remove_pointer_t<It>>::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<typename Other>
|
||||
requires (!stl::same_as<It, Other> && stl::constructible_from<It, Other>)
|
||||
constexpr dense_set_local_iterator(const dense_set_local_iterator<Other> &other) noexcept
|
||||
: it{other.it},
|
||||
offset{other.offset} {}
|
||||
|
||||
constexpr dense_set_local_iterator &operator++() noexcept {
|
||||
return offset = it[static_cast<difference_type>(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<difference_type>(offset)].second);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return *operator->();
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator<Other> &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<typename Type, typename Hash, typename KeyEqual, typename Allocator>
|
||||
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<stl::size_t, Type>;
|
||||
using alloc_traits = stl::allocator_traits<Allocator>;
|
||||
static_assert(stl::is_same_v<typename alloc_traits::value_type, Type>, "Invalid value type");
|
||||
using sparse_container_type = stl::vector<stl::size_t, typename alloc_traits::template rebind_alloc<stl::size_t>>;
|
||||
using packed_container_type = stl::vector<node_type, typename alloc_traits::template rebind_alloc<node_type>>;
|
||||
|
||||
[[nodiscard]] stl::size_t value_to_bucket(const auto &value) const noexcept {
|
||||
return fast_mod(static_cast<size_type>(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<iterator::difference_type>(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<const_iterator::difference_type>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
return cend();
|
||||
}
|
||||
|
||||
template<typename Other>
|
||||
[[nodiscard]] auto insert_or_do_nothing(Other &&value) {
|
||||
const auto index = value_to_bucket(value);
|
||||
|
||||
if(auto it = constrained_find(value, index); it != end()) {
|
||||
return stl::make_pair(it, false);
|
||||
}
|
||||
|
||||
packed.first().emplace_back(sparse.first()[index], stl::forward<Other>(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<size_type>(static_cast<float>(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<typename packed_container_type::pointer>;
|
||||
/*! @brief Constant random access iterator type. */
|
||||
using const_iterator = internal::dense_set_iterator<typename packed_container_type::const_pointer>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = stl::reverse_iterator<iterator>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
using const_reverse_iterator = stl::reverse_iterator<const_iterator>;
|
||||
/*! @brief Forward iterator type. */
|
||||
using local_iterator = internal::dense_set_local_iterator<typename packed_container_type::pointer>;
|
||||
/*! @brief Constant forward iterator type. */
|
||||
using const_local_iterator = internal::dense_set_local_iterator<typename packed_container_type::const_pointer>;
|
||||
|
||||
/*! @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<iterator, bool> insert(const value_type &value) {
|
||||
return insert_or_do_nothing(value);
|
||||
}
|
||||
|
||||
/*! @copydoc insert */
|
||||
stl::pair<iterator, bool> 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<typename... Args>
|
||||
stl::pair<iterator, bool> emplace(Args &&...args) {
|
||||
if constexpr(((sizeof...(Args) == 1u) && ... && stl::is_same_v<stl::decay_t<Args>, value_type>)) {
|
||||
return insert_or_do_nothing(stl::forward<Args>(args)...);
|
||||
} else {
|
||||
auto &node = packed.first().emplace_back(stl::piecewise_construct, stl::make_tuple(packed.first().size()), stl::forward_as_tuple(stl::forward<Args>(args)...));
|
||||
const auto index = value_to_bucket(node.second);
|
||||
|
||||
if(auto it = constrained_find(node.second, index); it != end()) {
|
||||
packed.first().pop_back();
|
||||
return 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<size_type>(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<hasher> && is_transparent_v<key_equal> {
|
||||
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<hasher> && is_transparent_v<key_equal> {
|
||||
return constrained_find(value, value_to_bucket(value));
|
||||
}
|
||||
|
||||
/*! @copydoc find */
|
||||
[[nodiscard]] const_iterator find(const auto &value) const
|
||||
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
|
||||
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<iterator, iterator> equal_range(const value_type &value) {
|
||||
const auto it = find(value);
|
||||
return {it, it + !(it == end())};
|
||||
}
|
||||
|
||||
/*! @copydoc equal_range */
|
||||
[[nodiscard]] stl::pair<const_iterator, const_iterator> 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<iterator, iterator> equal_range(const auto &value)
|
||||
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
|
||||
const auto it = find(value);
|
||||
return {it, it + !(it == end())};
|
||||
}
|
||||
|
||||
/*! @copydoc equal_range */
|
||||
[[nodiscard]] stl::pair<const_iterator, const_iterator> equal_range(const auto &value) const
|
||||
requires is_transparent_v<hasher> && is_transparent_v<key_equal> {
|
||||
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<hasher> && is_transparent_v<key_equal> {
|
||||
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<size_type>(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<float>(size()) / static_cast<float>(bucket_count());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the maximum average number of elements per bucket.
|
||||
* @return The maximum average number of elements per bucket.
|
||||
*/
|
||||
[[nodiscard]] float max_load_factor() const {
|
||||
return threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the desired maximum average number of elements per bucket.
|
||||
* @param value A desired maximum average number of elements per bucket.
|
||||
*/
|
||||
void max_load_factor(const float value) {
|
||||
ENTT_ASSERT(value > 0.f, "Invalid load factor");
|
||||
threshold = value;
|
||||
rehash(0u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reserves at least the specified number of buckets and regenerates
|
||||
* the hash table.
|
||||
* @param cnt New number of buckets.
|
||||
*/
|
||||
void rehash(const size_type cnt) {
|
||||
auto value = cnt > minimum_capacity ? cnt : minimum_capacity;
|
||||
const auto cap = static_cast<size_type>(static_cast<float>(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<size_type>(stl::ceil(static_cast<float>(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_container_type, hasher> sparse;
|
||||
compressed_pair<packed_container_type, key_equal> packed;
|
||||
float threshold{default_threshold};
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
38
include/entt/container/fwd.hpp
Normal file
38
include/entt/container/fwd.hpp
Normal file
@@ -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<Key>,
|
||||
typename = stl::equal_to<>,
|
||||
typename = stl::allocator<stl::pair<const Key, Type>>>
|
||||
class dense_map;
|
||||
|
||||
template<
|
||||
typename Type,
|
||||
typename = stl::hash<Type>,
|
||||
typename = stl::equal_to<>,
|
||||
typename = stl::allocator<Type>>
|
||||
class dense_set;
|
||||
|
||||
template<typename...>
|
||||
class basic_table;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Element types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
using table = basic_table<stl::vector<Type>...>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
434
include/entt/container/table.hpp
Normal file
434
include/entt/container/table.hpp
Normal file
@@ -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<typename... It>
|
||||
class table_iterator {
|
||||
template<typename...>
|
||||
friend class table_iterator;
|
||||
|
||||
public:
|
||||
using value_type = decltype(stl::forward_as_tuple(*stl::declval<It>()...));
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
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<typename... Other>
|
||||
requires (stl::constructible_from<It, Other> && ...)
|
||||
constexpr table_iterator(const table_iterator<Other...> &other) noexcept
|
||||
: table_iterator{stl::get<Other>(other.it)...} {}
|
||||
|
||||
constexpr table_iterator &operator++() noexcept {
|
||||
return (++stl::get<It>(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>(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>(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>(it)[value]...);
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr pointer operator->() const noexcept {
|
||||
return {operator[](0)};
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr reference operator*() const noexcept {
|
||||
return operator[](0);
|
||||
}
|
||||
|
||||
template<typename... Other>
|
||||
[[nodiscard]] constexpr stl::ptrdiff_t operator-(const table_iterator<Other...> &other) const noexcept {
|
||||
return stl::get<0>(it) - stl::get<0>(other.it);
|
||||
}
|
||||
|
||||
template<typename... Other>
|
||||
[[nodiscard]] constexpr bool operator==(const table_iterator<Other...> &other) const noexcept {
|
||||
return stl::get<0>(it) == stl::get<0>(other.it);
|
||||
}
|
||||
|
||||
template<typename... Other>
|
||||
[[nodiscard]] constexpr auto operator<=>(const table_iterator<Other...> &other) const noexcept {
|
||||
return stl::get<0>(it) <=> stl::get<0>(other.it);
|
||||
}
|
||||
|
||||
private:
|
||||
stl::tuple<It...> 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<typename... Container>
|
||||
class basic_table {
|
||||
using container_type = stl::tuple<Container...>;
|
||||
|
||||
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<typename Container::iterator...>;
|
||||
/*! @brief Constant input iterator type. */
|
||||
using const_iterator = internal::table_iterator<typename Container::const_iterator...>;
|
||||
/*! @brief Reverse iterator type. */
|
||||
using reverse_iterator = internal::table_iterator<typename Container::reverse_iterator...>;
|
||||
/*! @brief Constant reverse iterator type. */
|
||||
using const_reverse_iterator = internal::table_iterator<typename Container::const_reverse_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<Container>(payload).size() * sizeof...(Container)) == (stl::get<Container>(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<Container>(payload).size() * sizeof...(Container)) == (stl::get<Container>(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<class Allocator>
|
||||
basic_table(const Container &...container, const Allocator &allocator) noexcept
|
||||
: payload{Container{container, allocator}...} {
|
||||
ENTT_ASSERT((((stl::get<Container>(payload).size() * sizeof...(Container)) == (stl::get<Container>(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<class Allocator>
|
||||
basic_table(Container &&...container, const Allocator &allocator) noexcept
|
||||
: payload{Container{stl::move(container), allocator}...} {
|
||||
ENTT_ASSERT((((stl::get<Container>(payload).size() * sizeof...(Container)) == (stl::get<Container>(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<class Allocator>
|
||||
basic_table(basic_table &&other, const Allocator &allocator)
|
||||
: payload{Container{stl::move(stl::get<Container>(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<Container>(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<Container>(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<Container>(payload).cbegin()...};
|
||||
}
|
||||
|
||||
/*! @copydoc cbegin */
|
||||
[[nodiscard]] const_iterator begin() const noexcept {
|
||||
return cbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc begin */
|
||||
[[nodiscard]] iterator begin() noexcept {
|
||||
return {stl::get<Container>(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<Container>(payload).cend()...};
|
||||
}
|
||||
|
||||
/*! @copydoc cend */
|
||||
[[nodiscard]] const_iterator end() const noexcept {
|
||||
return cend();
|
||||
}
|
||||
|
||||
/*! @copydoc end */
|
||||
[[nodiscard]] iterator end() noexcept {
|
||||
return {stl::get<Container>(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<Container>(payload).crbegin()...};
|
||||
}
|
||||
|
||||
/*! @copydoc crbegin */
|
||||
[[nodiscard]] const_reverse_iterator rbegin() const noexcept {
|
||||
return crbegin();
|
||||
}
|
||||
|
||||
/*! @copydoc rbegin */
|
||||
[[nodiscard]] reverse_iterator rbegin() noexcept {
|
||||
return {stl::get<Container>(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<Container>(payload).crend()...};
|
||||
}
|
||||
|
||||
/*! @copydoc crend */
|
||||
[[nodiscard]] const_reverse_iterator rend() const noexcept {
|
||||
return crend();
|
||||
}
|
||||
|
||||
/*! @copydoc rend */
|
||||
[[nodiscard]] reverse_iterator rend() noexcept {
|
||||
return {stl::get<Container>(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<typename... Args>
|
||||
stl::tuple<typename Container::value_type &...> emplace(Args &&...args) {
|
||||
if constexpr(sizeof...(Args) == 0u) {
|
||||
return stl::forward_as_tuple(stl::get<Container>(payload).emplace_back()...);
|
||||
} else {
|
||||
return stl::forward_as_tuple(stl::get<Container>(payload).emplace_back(stl::forward<Args>(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<Container>(payload).erase(stl::get<Container>(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<difference_type>(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<const typename Container::value_type &...> operator[](const size_type pos) const {
|
||||
ENTT_ASSERT(pos < size(), "Index out of bounds");
|
||||
return stl::forward_as_tuple(stl::get<Container>(payload)[pos]...);
|
||||
}
|
||||
|
||||
/*! @copydoc operator[] */
|
||||
[[nodiscard]] stl::tuple<typename Container::value_type &...> operator[](const size_type pos) {
|
||||
ENTT_ASSERT(pos < size(), "Index out of bounds");
|
||||
return stl::forward_as_tuple(stl::get<Container>(payload)[pos]...);
|
||||
}
|
||||
|
||||
/*! @brief Clears a table. */
|
||||
void clear() {
|
||||
(stl::get<Container>(payload).clear(), ...);
|
||||
}
|
||||
|
||||
private:
|
||||
container_type payload;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
/*! @cond ENTT_INTERNAL */
|
||||
#include <utility>
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename... Container, typename Allocator>
|
||||
struct uses_allocator<entt::basic_table<Container...>, Allocator>
|
||||
: entt::stl::bool_constant<(entt::stl::uses_allocator_v<Container, Allocator> && ...)> {};
|
||||
|
||||
} // namespace std
|
||||
/*! @endcond */
|
||||
|
||||
#endif
|
||||
143
include/entt/core/algorithm.hpp
Normal file
143
include/entt/core/algorithm.hpp
Normal file
@@ -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.<br/>
|
||||
* 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 Compare = stl::less<>, 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>(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<typename Compare = stl::less<>>
|
||||
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<stl::size_t Bit, stl::size_t N>
|
||||
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<stl::random_access_iterator It, typename Getter = stl::identity>
|
||||
void operator()(It first, It last, Getter getter = Getter{}) const {
|
||||
if(first < last) {
|
||||
constexpr auto passes = N / Bit;
|
||||
|
||||
using value_type = stl::iterator_traits<It>::value_type;
|
||||
using difference_type = stl::iterator_traits<It>::difference_type;
|
||||
stl::vector<value_type> aux(static_cast<stl::size_t>(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<difference_type>(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
|
||||
623
include/entt/core/any.hpp
Normal file
623
include/entt/core/any.hpp
Normal file
@@ -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<stl::size_t Len, stl::size_t Align>
|
||||
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<stl::size_t Align>
|
||||
struct basic_any_storage<0u, Align> {
|
||||
static constexpr bool has_buffer = false;
|
||||
const void *instance{};
|
||||
};
|
||||
|
||||
template<typename Type, stl::size_t Len, stl::size_t Align>
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression)
|
||||
struct in_situ: stl::bool_constant<(Len != 0u) && alignof(Type) <= Align && sizeof(Type) <= Len && stl::is_nothrow_move_constructible_v<Type>> {};
|
||||
|
||||
template<stl::size_t Len, stl::size_t Align>
|
||||
struct in_situ<void, Len, Align>: 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<stl::size_t Len, stl::size_t Align>
|
||||
class basic_any: private internal::basic_any_storage<Len, Align> {
|
||||
using request = internal::any_request;
|
||||
using base_type = internal::basic_any_storage<Len, Align>;
|
||||
using vtable_type = const void *(const request, const basic_any &, const void *);
|
||||
using deleter_type = void(const basic_any &);
|
||||
|
||||
template<typename Type>
|
||||
static constexpr bool in_situ_v = internal::in_situ<Type, Len, Align>::value;
|
||||
|
||||
template<cvref_unqualified Type>
|
||||
static const void *basic_vtable(const request req, const basic_any &value, const void *other) {
|
||||
switch(const auto *elem = static_cast<const Type *>(value.data()); req) {
|
||||
using enum internal::any_request;
|
||||
case info:
|
||||
return &type_id<Type>();
|
||||
case transfer:
|
||||
if constexpr(stl::is_move_assignable_v<Type>) {
|
||||
// NOLINTNEXTLINE(bugprone-casting-through-void)
|
||||
*const_cast<Type *>(elem) = stl::move(*static_cast<Type *>(const_cast<void *>(other)));
|
||||
return other;
|
||||
}
|
||||
[[fallthrough]];
|
||||
case assign:
|
||||
if constexpr(stl::is_copy_assignable_v<Type>) {
|
||||
*const_cast<Type *>(elem) = *static_cast<const Type *>(other);
|
||||
return other;
|
||||
}
|
||||
break;
|
||||
case compare:
|
||||
if constexpr(!stl::is_function_v<Type> && !stl::is_array_v<Type> && is_equality_comparable_v<Type>) {
|
||||
return (*elem == *static_cast<const Type *>(other)) ? other : nullptr;
|
||||
} else {
|
||||
return (elem == other) ? other : nullptr;
|
||||
}
|
||||
case copy:
|
||||
if constexpr(stl::is_copy_constructible_v<Type>) {
|
||||
// NOLINTNEXTLINE(bugprone-casting-through-void)
|
||||
static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*elem);
|
||||
}
|
||||
break;
|
||||
case move:
|
||||
ENTT_ASSERT(value.mode == any_policy::embedded, "Unexpected policy");
|
||||
if constexpr(in_situ_v<Type>) {
|
||||
// NOLINTNEXTLINE(bugprone-casting-through-void, bugprone-multi-level-implicit-pointer-conversion)
|
||||
return ::new(&static_cast<basic_any *>(const_cast<void *>(other))->buffer) Type{stl::move(*const_cast<Type *>(elem))};
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<cvref_unqualified Type>
|
||||
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<Type>), "Unexpected policy");
|
||||
|
||||
const auto *elem = static_cast<const Type *>(value.data());
|
||||
|
||||
if constexpr(in_situ_v<Type>) {
|
||||
(value.mode == any_policy::embedded) ? elem->~Type() : (delete elem);
|
||||
} else if constexpr(stl::is_array_v<Type>) {
|
||||
delete[] elem;
|
||||
} else {
|
||||
delete elem;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Type, typename... Args>
|
||||
void initialize([[maybe_unused]] Args &&...args) {
|
||||
using plain_type = stl::remove_cvref_t<Type>;
|
||||
|
||||
vtable = basic_vtable<plain_type>;
|
||||
underlying_type = type_hash<plain_type>::value();
|
||||
|
||||
if constexpr(stl::is_void_v<Type>) {
|
||||
deleter = nullptr;
|
||||
mode = any_policy::empty;
|
||||
this->instance = nullptr;
|
||||
} else if constexpr(stl::is_lvalue_reference_v<Type>) {
|
||||
deleter = nullptr;
|
||||
mode = stl::is_const_v<stl::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
|
||||
static_assert((stl::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
|
||||
// NOLINTNEXTLINE(bugprone-multi-level-implicit-pointer-conversion)
|
||||
this->instance = (stl::addressof(args), ...);
|
||||
} else if constexpr(in_situ_v<plain_type>) {
|
||||
if constexpr(stl::is_trivially_destructible_v<plain_type>) {
|
||||
deleter = nullptr;
|
||||
} else {
|
||||
deleter = &basic_deleter<plain_type>;
|
||||
}
|
||||
|
||||
mode = any_policy::embedded;
|
||||
|
||||
if constexpr(stl::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !stl::is_default_constructible_v<plain_type>)) {
|
||||
::new(&this->buffer) plain_type{stl::forward<Args>(args)...};
|
||||
} else {
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
|
||||
::new(&this->buffer) plain_type(stl::forward<Args>(args)...);
|
||||
}
|
||||
} else {
|
||||
deleter = &basic_deleter<plain_type>;
|
||||
mode = any_policy::dynamic;
|
||||
|
||||
if constexpr(stl::is_aggregate_v<plain_type> && (sizeof...(Args) != 0u || !stl::is_default_constructible_v<plain_type>)) {
|
||||
this->instance = new plain_type{stl::forward<Args>(args)...};
|
||||
} else if constexpr(stl::is_array_v<plain_type>) {
|
||||
static_assert(sizeof...(Args) == 0u, "Invalid arguments");
|
||||
this->instance = new plain_type[stl::extent_v<plain_type>]();
|
||||
} else {
|
||||
this->instance = new plain_type(stl::forward<Args>(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<void>} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a wrapper by directly initializing the new object.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
explicit basic_any(stl::in_place_type_t<Type>, Args &&...args)
|
||||
: base_type{} {
|
||||
initialize<Type>(stl::forward<Args>(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<typename Type>
|
||||
requires (!stl::is_const_v<Type> && !stl::is_void_v<Type>)
|
||||
explicit basic_any(stl::in_place_t, Type *value)
|
||||
: base_type{} {
|
||||
if(value == nullptr) {
|
||||
initialize<void>();
|
||||
} else {
|
||||
initialize<Type &>(*value);
|
||||
deleter = &basic_deleter<Type>;
|
||||
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<typename Type>
|
||||
requires (!stl::same_as<stl::remove_cvref_t<Type>, basic_any>)
|
||||
basic_any(Type &&value)
|
||||
: basic_any{stl::in_place_type<stl::decay_t<Type>>, stl::forward<Type>(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<void>();
|
||||
}
|
||||
}
|
||||
|
||||
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<typename Type>
|
||||
requires (!stl::same_as<stl::remove_cvref_t<Type>, basic_any>)
|
||||
basic_any &operator=(Type &&value) {
|
||||
emplace<stl::decay_t<Type>>(stl::forward<Type>(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<cvref_unqualified Type>
|
||||
[[nodiscard]] bool has_value() const noexcept {
|
||||
return (underlying_type == type_hash<Type>::value());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the object type info if any, `type_id<void>()` otherwise.
|
||||
* @return The object type info if any, `type_id<void>()` otherwise.
|
||||
*/
|
||||
[[nodiscard]] const type_info &info() const noexcept {
|
||||
return *static_cast<const type_info *>(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<typename Type>
|
||||
[[nodiscard]] const Type *data() const noexcept {
|
||||
return has_value<stl::remove_const_t<Type>>() ? static_cast<const Type *>(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<void *>(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<void *>(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<typename Type>
|
||||
[[nodiscard]] Type *data() noexcept {
|
||||
if constexpr(stl::is_const_v<Type>) {
|
||||
return stl::as_const(*this).template data<stl::remove_const_t<Type>>();
|
||||
} else {
|
||||
return (mode == any_policy::cref) ? nullptr : const_cast<Type *>(stl::as_const(*this).template data<stl::remove_const_t<Type>>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the contained object by creating a new instance directly.
|
||||
* @tparam Type Type of object to use to initialize the wrapper.
|
||||
* @tparam Args Types of arguments to use to construct the new instance.
|
||||
* @param args Parameters to use to construct the instance.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
void emplace(Args &&...args) {
|
||||
invoke_deleter_if_exists();
|
||||
initialize<Type>(stl::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assigns a value to the contained object without replacing it.
|
||||
* @param other The value to assign to the contained object.
|
||||
* @return True in case of success, false otherwise.
|
||||
*/
|
||||
bool assign(const 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<void>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @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<typename Type, stl::size_t Len, stl::size_t Align>
|
||||
[[nodiscard]] stl::remove_const_t<Type> any_cast(const basic_any<Len, Align> &data) noexcept {
|
||||
const auto *const instance = any_cast<stl::remove_reference_t<Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
return static_cast<Type>(*instance);
|
||||
}
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, stl::size_t Len, stl::size_t Align>
|
||||
[[nodiscard]] stl::remove_const_t<Type> any_cast(basic_any<Len, Align> &data) noexcept {
|
||||
// forces const on non-reference types to make them work also with wrappers for const references
|
||||
auto *const instance = any_cast<stl::remove_reference_t<const Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
return static_cast<Type>(*instance);
|
||||
}
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, stl::size_t Len, stl::size_t Align>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved)
|
||||
[[nodiscard]] stl::remove_const_t<Type> any_cast(basic_any<Len, Align> &&data) noexcept {
|
||||
if constexpr(stl::is_copy_constructible_v<stl::remove_cvref_t<Type>>) {
|
||||
if(auto *const instance = any_cast<stl::remove_reference_t<Type>>(&data); instance) {
|
||||
return static_cast<Type>(stl::move(*instance));
|
||||
}
|
||||
|
||||
return any_cast<Type>(data);
|
||||
} else {
|
||||
auto *const instance = any_cast<stl::remove_reference_t<Type>>(&data);
|
||||
ENTT_ASSERT(instance, "Invalid instance");
|
||||
return static_cast<Type>(stl::move(*instance));
|
||||
}
|
||||
}
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, stl::size_t Len, stl::size_t Align>
|
||||
[[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
|
||||
return data->template data<stl::remove_const_t<Type>>();
|
||||
}
|
||||
|
||||
/*! @copydoc any_cast */
|
||||
template<typename Type, stl::size_t Len, stl::size_t Align>
|
||||
[[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
|
||||
if constexpr(stl::is_const_v<Type>) {
|
||||
// last attempt to make wrappers for const references return their values
|
||||
return any_cast<Type>(&stl::as_const(*data));
|
||||
} else {
|
||||
return data->template data<Type>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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<typename Type, stl::size_t Len = basic_any<>::length, stl::size_t Align = basic_any<Len>::alignment, typename... Args>
|
||||
[[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
|
||||
return basic_any<Len, Align>{stl::in_place_type<Type>, stl::forward<Args>(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<stl::size_t Len = basic_any<>::length, stl::size_t Align = basic_any<Len>::alignment, typename Type>
|
||||
[[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
|
||||
return basic_any<Len, Align>{stl::in_place_type<Type &&>, stl::forward<Type>(value)};
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
26
include/entt/core/bit.hpp
Normal file
26
include/entt/core/bit.hpp
Normal file
@@ -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<stl::unsigned_integral Type>
|
||||
[[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<Type>(value & (mod - 1u));
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
266
include/entt/core/compressed_pair.hpp
Normal file
266
include/entt/core/compressed_pair.hpp
Normal file
@@ -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<typename Type, stl::size_t>
|
||||
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<Type>)
|
||||
requires stl::default_initializable<Type> {}
|
||||
|
||||
template<typename Arg>
|
||||
constexpr compressed_pair_element(Arg &&arg) noexcept(stl::is_nothrow_constructible_v<Type, Arg>)
|
||||
requires (!stl::same_as<stl::remove_cvref_t<Arg>, compressed_pair_element>)
|
||||
: value{stl::forward<Arg>(arg)} {}
|
||||
|
||||
template<typename... Args, stl::size_t... Index>
|
||||
constexpr compressed_pair_element(stl::tuple<Args...> args, stl::index_sequence<Index...>) noexcept(stl::is_nothrow_constructible_v<Type, Args...>)
|
||||
: value{stl::forward<Args>(stl::get<Index>(args))...} {}
|
||||
|
||||
[[nodiscard]] constexpr reference get() noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr const_reference get() const noexcept {
|
||||
return value;
|
||||
}
|
||||
|
||||
private:
|
||||
Type value{};
|
||||
};
|
||||
|
||||
template<typename Type, stl::size_t Tag>
|
||||
requires is_ebco_eligible_v<Type>
|
||||
struct compressed_pair_element<Type, Tag>: Type {
|
||||
using reference = Type &;
|
||||
using const_reference = const Type &;
|
||||
using base_type = Type;
|
||||
|
||||
constexpr compressed_pair_element() noexcept(stl::is_nothrow_default_constructible_v<base_type>)
|
||||
requires stl::default_initializable<Type>
|
||||
: base_type{} {}
|
||||
|
||||
template<typename Arg>
|
||||
constexpr compressed_pair_element(Arg &&arg) noexcept(stl::is_nothrow_constructible_v<base_type, Arg>)
|
||||
requires (!stl::same_as<stl::remove_cvref_t<Arg>, compressed_pair_element>)
|
||||
: base_type{stl::forward<Arg>(arg)} {}
|
||||
|
||||
template<typename... Args, stl::size_t... Index>
|
||||
constexpr compressed_pair_element(stl::tuple<Args...> args, stl::index_sequence<Index...>) noexcept(stl::is_nothrow_constructible_v<base_type, Args...>)
|
||||
: base_type{stl::forward<Args>(stl::get<Index>(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<typename First, typename Second>
|
||||
class compressed_pair final
|
||||
: internal::compressed_pair_element<First, 0u>,
|
||||
internal::compressed_pair_element<Second, 1u> {
|
||||
using first_base = internal::compressed_pair_element<First, 0u>;
|
||||
using second_base = internal::compressed_pair_element<Second, 1u>;
|
||||
|
||||
public:
|
||||
/*! @brief The type of the first element that the pair stores. */
|
||||
using first_type = First;
|
||||
/*! @brief The type of the second element that the pair stores. */
|
||||
using second_type = Second;
|
||||
|
||||
/**
|
||||
* @brief Default constructor, conditionally enabled.
|
||||
*
|
||||
* This constructor is only available when the types that the pair stores
|
||||
* are both at least default constructible.
|
||||
*/
|
||||
constexpr compressed_pair() noexcept(stl::is_nothrow_default_constructible_v<first_base> && stl::is_nothrow_default_constructible_v<second_base>)
|
||||
requires stl::default_initializable<first_type> && stl::default_initializable<second_type>
|
||||
: 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<typename Arg, typename Other>
|
||||
constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(stl::is_nothrow_constructible_v<first_base, Arg> && stl::is_nothrow_constructible_v<second_base, Other>)
|
||||
: first_base{stl::forward<Arg>(arg)},
|
||||
second_base{stl::forward<Other>(other)} {}
|
||||
|
||||
/**
|
||||
* @brief Constructs a pair by forwarding the arguments to its parts.
|
||||
* @tparam Args Types of arguments to use to initialize the first element.
|
||||
* @tparam Other Types of arguments to use to initialize the second element.
|
||||
* @param args Arguments to use to initialize the first element.
|
||||
* @param other Arguments to use to initialize the second element.
|
||||
*/
|
||||
template<typename... Args, typename... Other>
|
||||
constexpr compressed_pair(stl::piecewise_construct_t, stl::tuple<Args...> args, stl::tuple<Other...> other) noexcept(stl::is_nothrow_constructible_v<first_base, Args...> && stl::is_nothrow_constructible_v<second_base, Other...>)
|
||||
: first_base{stl::move(args), stl::index_sequence_for<Args...>{}},
|
||||
second_base{stl::move(other), stl::index_sequence_for<Other...>{}} {}
|
||||
|
||||
/*! @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<first_base &>(*this).get();
|
||||
}
|
||||
|
||||
/*! @copydoc first */
|
||||
[[nodiscard]] constexpr const first_type &first() const noexcept {
|
||||
return static_cast<const first_base &>(*this).get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the second element that a pair stores.
|
||||
* @return The second element that a pair stores.
|
||||
*/
|
||||
[[nodiscard]] constexpr second_type &second() noexcept {
|
||||
return static_cast<second_base &>(*this).get();
|
||||
}
|
||||
|
||||
/*! @copydoc second */
|
||||
[[nodiscard]] constexpr const second_type &second() const noexcept {
|
||||
return static_cast<const second_base &>(*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<stl::size_t Index>
|
||||
requires (Index <= 1u)
|
||||
[[nodiscard]] constexpr decltype(auto) get() noexcept {
|
||||
if constexpr(Index == 0u) {
|
||||
return first();
|
||||
} else {
|
||||
return second();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @copydoc get */
|
||||
template<stl::size_t Index>
|
||||
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<typename Type, typename Other>
|
||||
compressed_pair(Type &&, Other &&) -> compressed_pair<stl::decay_t<Type>, stl::decay_t<Other>>;
|
||||
|
||||
/**
|
||||
* @brief Swaps two compressed pair objects.
|
||||
* @tparam First The type of the first element that the pairs store.
|
||||
* @tparam Second The type of the second element that the pairs store.
|
||||
* @param lhs A valid compressed pair object.
|
||||
* @param rhs A valid compressed pair object.
|
||||
*/
|
||||
template<typename First, typename Second>
|
||||
constexpr void swap(compressed_pair<First, Second> &lhs, compressed_pair<First, Second> &rhs) noexcept {
|
||||
lhs.swap(rhs);
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
/*! @cond ENTT_INTERNAL */
|
||||
#include <utility>
|
||||
|
||||
namespace std {
|
||||
|
||||
template<typename First, typename Second>
|
||||
struct tuple_size<entt::compressed_pair<First, Second>>: integral_constant<entt::stl::size_t, 2u> {};
|
||||
|
||||
template<entt::stl::size_t Index, typename First, typename Second>
|
||||
requires (Index <= 1u)
|
||||
struct tuple_element<Index, entt::compressed_pair<First, Second>>: conditional<Index == 0u, First, Second> {};
|
||||
|
||||
} // namespace std
|
||||
/*! @endcond */
|
||||
|
||||
#endif
|
||||
17
include/entt/core/concepts.hpp
Normal file
17
include/entt/core/concepts.hpp
Normal file
@@ -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<typename Type>
|
||||
concept cvref_unqualified = stl::is_same_v<stl::remove_cvref_t<Type>, Type>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
102
include/entt/core/enum.hpp
Normal file
102
include/entt/core/enum.hpp
Normal file
@@ -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<typename Type>
|
||||
struct enum_as_bitmask: stl::false_type {};
|
||||
|
||||
/*! @copydoc enum_as_bitmask */
|
||||
template<typename Type>
|
||||
requires requires {
|
||||
requires stl::is_enum_v<Type>;
|
||||
{ Type::_entt_enum_as_bitmask } -> stl::same_as<Type>;
|
||||
}
|
||||
struct enum_as_bitmask<Type>: stl::true_type {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The enum class type for which to enable bitmask support.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool enum_as_bitmask_v = enum_as_bitmask<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Specifies that an enum class supports bitmask operations.
|
||||
* @tparam Type Enum class type.
|
||||
*/
|
||||
template<typename Type>
|
||||
// check again that it is an enum to deal with incorrect specializations
|
||||
concept enum_bitmask = stl::is_enum_v<Type> && enum_as_bitmask_v<Type>;
|
||||
|
||||
} // 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<entt::enum_bitmask Type>
|
||||
[[nodiscard]] constexpr Type operator|(const Type lhs, const Type rhs) noexcept {
|
||||
return static_cast<Type>(static_cast<entt::stl::underlying_type_t<Type>>(lhs) | static_cast<entt::stl::underlying_type_t<Type>>(rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<entt::enum_bitmask Type>
|
||||
[[nodiscard]] constexpr Type operator&(const Type lhs, const Type rhs) noexcept {
|
||||
return static_cast<Type>(static_cast<entt::stl::underlying_type_t<Type>>(lhs) & static_cast<entt::stl::underlying_type_t<Type>>(rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<entt::enum_bitmask Type>
|
||||
[[nodiscard]] constexpr Type operator^(const Type lhs, const Type rhs) noexcept {
|
||||
return static_cast<Type>(static_cast<entt::stl::underlying_type_t<Type>>(lhs) ^ static_cast<entt::stl::underlying_type_t<Type>>(rhs));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Operator available for enums for which bitmask support is enabled.
|
||||
* @tparam Type Enum class type.
|
||||
* @param value The value to use.
|
||||
* @return The result of invoking the operator on the underlying types of the
|
||||
* value provided.
|
||||
*/
|
||||
template<entt::enum_bitmask Type>
|
||||
[[nodiscard]] constexpr Type operator~(const Type value) noexcept {
|
||||
return static_cast<Type>(~static_cast<entt::stl::underlying_type_t<Type>>(value));
|
||||
}
|
||||
|
||||
/*! @copydoc operator~ */
|
||||
template<entt::enum_bitmask Type>
|
||||
[[nodiscard]] constexpr bool operator!(const Type value) noexcept {
|
||||
return !static_cast<entt::stl::underlying_type_t<Type>>(value);
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<entt::enum_bitmask Type>
|
||||
constexpr Type &operator|=(Type &lhs, const Type rhs) noexcept {
|
||||
return (lhs = (lhs | rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<entt::enum_bitmask Type>
|
||||
constexpr Type &operator&=(Type &lhs, const Type rhs) noexcept {
|
||||
return (lhs = (lhs & rhs));
|
||||
}
|
||||
|
||||
/*! @copydoc operator| */
|
||||
template<entt::enum_bitmask Type>
|
||||
constexpr Type &operator^=(Type &lhs, const Type rhs) noexcept {
|
||||
return (lhs = (lhs ^ rhs));
|
||||
}
|
||||
|
||||
#endif
|
||||
35
include/entt/core/family.hpp
Normal file
35
include/entt/core/family.hpp
Normal file
@@ -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<typename...>
|
||||
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<typename... Type>
|
||||
// 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
|
||||
51
include/entt/core/fwd.hpp
Normal file
51
include/entt/core/fwd.hpp
Normal file
@@ -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<stl::size_t Len = sizeof(double[2]), stl::size_t = alignof(double[2])>
|
||||
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<typename, typename>
|
||||
class compressed_pair;
|
||||
|
||||
template<typename>
|
||||
class basic_hashed_string;
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_string = basic_hashed_string<char>;
|
||||
|
||||
/*! @brief Aliases for common character types. */
|
||||
using hashed_wstring = basic_hashed_string<wchar_t>;
|
||||
|
||||
// NOLINTNEXTLINE(bugprone-forward-declaration-namespace)
|
||||
struct type_info;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
260
include/entt/core/hashed_string.hpp
Normal file
260
include/entt/core/hashed_string.hpp
Normal file
@@ -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<typename = id_type>
|
||||
struct fnv_1a_params;
|
||||
|
||||
template<>
|
||||
struct fnv_1a_params<stl::uint32_t> {
|
||||
static constexpr auto offset = 2166136261;
|
||||
static constexpr auto prime = 16777619;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct fnv_1a_params<stl::uint64_t> {
|
||||
static constexpr auto offset = 14695981039346656037ull;
|
||||
static constexpr auto prime = 1099511628211ull;
|
||||
};
|
||||
|
||||
template<typename Char>
|
||||
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.<br/>
|
||||
* Because of that, a hashed string can also be used in constant expressions if
|
||||
* required.
|
||||
*
|
||||
* @warning
|
||||
* This class doesn't take ownership of user-supplied strings nor does it make a
|
||||
* copy of them.
|
||||
*
|
||||
* @tparam Char Character type.
|
||||
*/
|
||||
template<typename Char>
|
||||
class basic_hashed_string: internal::basic_hashed_string<Char> {
|
||||
using base_type = internal::basic_hashed_string<Char>;
|
||||
using 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<stl::size_t N>
|
||||
// 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<id_type>(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<stl::size_t N>
|
||||
// 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<id_type>(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<id_type>(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<typename Char>
|
||||
basic_hashed_string(const Char *str, stl::size_t len) -> basic_hashed_string<Char>;
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Char Character type.
|
||||
* @tparam N Number of characters of the identifier.
|
||||
* @param str Human-readable identifier.
|
||||
*/
|
||||
template<typename Char, stl::size_t N>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays, modernize-avoid-c-arrays)
|
||||
basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string<Char>;
|
||||
|
||||
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
|
||||
35
include/entt/core/ident.hpp
Normal file
35
include/entt/core/ident.hpp
Normal file
@@ -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<typename... Type>
|
||||
class ident {
|
||||
template<typename Curr, stl::size_t... Index>
|
||||
[[nodiscard]] static ENTT_CONSTEVAL id_type get(stl::index_sequence<Index...>) noexcept {
|
||||
return (0 + ... + (stl::is_same_v<Curr, type_list_element_t<Index, type_list<stl::decay_t<Type>...>>> ? 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<typename Curr>
|
||||
requires (stl::is_same_v<stl::remove_cvref_t<Curr>, Type> || ...)
|
||||
static constexpr value_type value = get<stl::remove_cvref_t<Curr>>(stl::index_sequence_for<Type...>{});
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
181
include/entt/core/iterator.hpp
Normal file
181
include/entt/core/iterator.hpp
Normal file
@@ -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<typename Type>
|
||||
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_type>)
|
||||
: 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<stl::integral Type>
|
||||
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<stl::input_or_output_iterator It, stl::sentinel_for<It> Sentinel = It>
|
||||
struct iterable_adaptor final {
|
||||
/*! @brief Value type. */
|
||||
using value_type = stl::iterator_traits<It>::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<iterator> && stl::is_nothrow_default_constructible_v<sentinel>)
|
||||
: 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<iterator> && stl::is_nothrow_move_constructible_v<sentinel>)
|
||||
: 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
|
||||
225
include/entt/core/memory.hpp
Normal file
225
include/entt/core/memory.hpp
Normal file
@@ -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<typename Allocator>
|
||||
constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
|
||||
if constexpr(stl::allocator_traits<Allocator>::propagate_on_container_copy_assignment::value) {
|
||||
lhs = rhs;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility function to design allocation-aware containers.
|
||||
* @tparam Allocator Type of allocator.
|
||||
* @param lhs A valid allocator.
|
||||
* @param rhs Another valid allocator.
|
||||
*/
|
||||
template<typename Allocator>
|
||||
constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
|
||||
if constexpr(stl::allocator_traits<Allocator>::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<typename Allocator>
|
||||
constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept {
|
||||
if constexpr(stl::allocator_traits<Allocator>::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<typename Allocator>
|
||||
struct allocation_deleter: private Allocator {
|
||||
/*! @brief Allocator type. */
|
||||
using allocator_type = Allocator;
|
||||
/*! @brief Pointer type. */
|
||||
using pointer = stl::allocator_traits<Allocator>::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_type>)
|
||||
: 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<typename allocator_type::value_type>) {
|
||||
using alloc_traits = stl::allocator_traits<Allocator>;
|
||||
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<typename Type, typename Allocator, typename... Args>
|
||||
constexpr auto allocate_unique(Allocator &allocator, Args &&...args) {
|
||||
static_assert(!stl::is_array_v<Type>, "Array types are not supported");
|
||||
|
||||
using alloc_traits = stl::allocator_traits<Allocator>::template rebind_traits<Type>;
|
||||
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>(args)...);
|
||||
}
|
||||
ENTT_CATCH {
|
||||
alloc_traits::deallocate(alloc, ptr, 1u);
|
||||
ENTT_THROW;
|
||||
}
|
||||
|
||||
return stl::unique_ptr<Type, allocation_deleter<allocator_type>>{ptr, alloc};
|
||||
}
|
||||
|
||||
/*! @cond ENTT_INTERNAL */
|
||||
namespace internal {
|
||||
|
||||
template<typename Type>
|
||||
struct uses_allocator_construction {
|
||||
template<typename Allocator, typename... Params>
|
||||
static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept {
|
||||
if constexpr(!stl::uses_allocator_v<Type, Allocator> && stl::is_constructible_v<Type, Params...>) {
|
||||
return stl::forward_as_tuple(stl::forward<Params>(params)...);
|
||||
} else {
|
||||
static_assert(stl::uses_allocator_v<Type, Allocator>, "Ill-formed request");
|
||||
|
||||
if constexpr(stl::is_constructible_v<Type, stl::allocator_arg_t, const Allocator &, Params...>) {
|
||||
return stl::tuple<stl::allocator_arg_t, const Allocator &, Params &&...>{stl::allocator_arg, allocator, stl::forward<Params>(params)...};
|
||||
} else {
|
||||
static_assert(stl::is_constructible_v<Type, Params..., const Allocator &>, "Ill-formed request");
|
||||
return stl::forward_as_tuple(stl::forward<Params>(params)..., allocator);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Type, typename Other>
|
||||
struct uses_allocator_construction<stl::pair<Type, Other>> {
|
||||
using type = stl::pair<Type, Other>;
|
||||
|
||||
template<typename First, typename Second>
|
||||
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<Type>::args(allocator, stl::forward<decltype(curr)>(curr)...); }, stl::forward<First>(first)),
|
||||
stl::apply([&allocator](auto &&...curr) { return uses_allocator_construction<Other>::args(allocator, stl::forward<decltype(curr)>(curr)...); }, stl::forward<Second>(second)));
|
||||
}
|
||||
|
||||
static constexpr auto args(const auto &allocator) noexcept {
|
||||
return uses_allocator_construction<type>::args(allocator, stl::piecewise_construct, stl::tuple<>{}, stl::tuple<>{});
|
||||
}
|
||||
|
||||
template<typename First, typename Second>
|
||||
static constexpr auto args(const auto &allocator, First &&first, Second &&second) noexcept {
|
||||
return uses_allocator_construction<type>::args(allocator, stl::piecewise_construct, stl::forward_as_tuple(stl::forward<First>(first)), stl::forward_as_tuple(stl::forward<Second>(second)));
|
||||
}
|
||||
|
||||
template<typename First, typename Second>
|
||||
static constexpr auto args(const auto &allocator, const stl::pair<First, Second> &value) noexcept {
|
||||
return uses_allocator_construction<type>::args(allocator, stl::piecewise_construct, stl::forward_as_tuple(value.first), stl::forward_as_tuple(value.second));
|
||||
}
|
||||
|
||||
template<typename First, typename Second>
|
||||
static constexpr auto args(const auto &allocator, stl::pair<First, Second> &&value) noexcept {
|
||||
return uses_allocator_construction<type>::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<typename Type, typename... Args>
|
||||
constexpr auto uses_allocator_construction_args(const auto &allocator, Args &&...args) noexcept {
|
||||
return internal::uses_allocator_construction<Type>::args(allocator, stl::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Uses-allocator construction utility (waiting for C++20).
|
||||
*
|
||||
* Primarily intended for internal use. Creates an object of a given type by
|
||||
* means of uses-allocator construction.
|
||||
*
|
||||
* @tparam Type Type of object to create.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param allocator The allocator to use.
|
||||
* @param args Parameters to use to construct the object.
|
||||
* @return A newly created object of the given type.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
constexpr Type make_obj_using_allocator(const auto &allocator, Args &&...args) {
|
||||
return stl::make_from_tuple<Type>(internal::uses_allocator_construction<Type>::args(allocator, stl::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Uses-allocator construction utility (waiting for C++20).
|
||||
*
|
||||
* Primarily intended for internal use. Creates an object of a given type by
|
||||
* means of uses-allocator construction at an uninitialized memory location.
|
||||
*
|
||||
* @tparam Type Type of object to create.
|
||||
* @tparam Args Types of arguments to use to construct the object.
|
||||
* @param value Memory location in which to place the object.
|
||||
* @param allocator The allocator to use.
|
||||
* @param args Parameters to use to construct the object.
|
||||
* @return A pointer to the newly created object of the given type.
|
||||
*/
|
||||
template<typename Type, typename... Args>
|
||||
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<decltype(curr)>(curr)...); }, internal::uses_allocator_construction<Type>::args(allocator, stl::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
60
include/entt/core/monostate.hpp
Normal file
60
include/entt/core/monostate.hpp
Normal file
@@ -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.<br/>
|
||||
* Multiple types and therefore more than one value can be associated with a
|
||||
* single key. Because of this, users must pay attention to use the same type
|
||||
* both during an assignment and when they try to read back their data.
|
||||
* Otherwise, they can incur in unexpected results.
|
||||
*/
|
||||
template<id_type>
|
||||
struct monostate {
|
||||
/**
|
||||
* @brief Assigns a value of a specific type to a given key.
|
||||
* @tparam Type Type of the value to assign.
|
||||
* @param val User data to assign to the given key.
|
||||
* @return This monostate object.
|
||||
*/
|
||||
template<typename Type>
|
||||
monostate &operator=(Type val) noexcept {
|
||||
value<Type> = 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<typename Type>
|
||||
operator Type() const noexcept {
|
||||
return value<Type>;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Type>
|
||||
// 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<id_type Value>
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||
inline monostate<Value> monostate_v{};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
22
include/entt/core/ranges.hpp
Normal file
22
include/entt/core/ranges.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#ifndef ENTT_CORE_RANGES_HPP
|
||||
#define ENTT_CORE_RANGES_HPP
|
||||
|
||||
#include <version>
|
||||
|
||||
#if defined(__cpp_lib_ranges)
|
||||
# include <ranges>
|
||||
# include "iterator.hpp"
|
||||
|
||||
namespace std::ranges {
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool enable_borrowed_range<entt::iterable_adaptor<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool enable_view<entt::iterable_adaptor<Args...>>{true};
|
||||
|
||||
} // namespace std::ranges
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
90
include/entt/core/tuple.hpp
Normal file
90
include/entt/core/tuple.hpp
Normal file
@@ -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<typename Type>
|
||||
struct is_tuple: stl::false_type {};
|
||||
|
||||
/**
|
||||
* @copybrief is_tuple
|
||||
* @tparam Args Tuple template arguments.
|
||||
*/
|
||||
template<typename... Args>
|
||||
struct is_tuple<stl::tuple<Args...>>: stl::true_type {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_tuple_v = is_tuple<Type>::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<typename Type>
|
||||
constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept {
|
||||
if constexpr(stl::tuple_size_v<stl::remove_reference_t<Type>> == 1u) {
|
||||
return stl::get<0>(stl::forward<Type>(value));
|
||||
} else {
|
||||
return stl::forward<Type>(value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Utility class to forward-and-apply tuple objects.
|
||||
* @tparam Func Type of underlying invocable object.
|
||||
*/
|
||||
template<typename Func>
|
||||
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<typename... Args>
|
||||
constexpr forward_apply(Args &&...args) noexcept(stl::is_nothrow_constructible_v<Func, Args...>)
|
||||
: Func{stl::forward<Args>(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<typename Type>
|
||||
constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(stl::apply(stl::declval<Func &>(), args))) {
|
||||
return stl::apply(static_cast<Func &>(*this), stl::forward<Type>(args));
|
||||
}
|
||||
|
||||
/*! @copydoc operator()() */
|
||||
template<typename Type>
|
||||
constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(stl::apply(stl::declval<const Func &>(), args))) {
|
||||
return stl::apply(static_cast<const Func &>(*this), stl::forward<Type>(args));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Func Type of underlying invocable object.
|
||||
*/
|
||||
template<typename Func>
|
||||
forward_apply(Func) -> forward_apply<stl::remove_cvref_t<Func>>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
232
include/entt/core/type_info.hpp
Normal file
232
include/entt/core/type_info.hpp
Normal file
@@ -0,0 +1,232 @@
|
||||
#ifndef ENTT_CORE_TYPE_INFO_HPP
|
||||
#define ENTT_CORE_TYPE_INFO_HPP
|
||||
|
||||
#include <compare>
|
||||
#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<typename Type>
|
||||
[[nodiscard]] constexpr const char *pretty_function() noexcept {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
return static_cast<const char *>(ENTT_PRETTY_FUNCTION);
|
||||
#else
|
||||
return "";
|
||||
#endif
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] constexpr auto stripped_type_name() noexcept {
|
||||
#if defined ENTT_PRETTY_FUNCTION
|
||||
const stl::string_view full_name{pretty_function<Type>()};
|
||||
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<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
|
||||
[[nodiscard]] ENTT_CONSTEVAL stl::string_view type_name(int) noexcept {
|
||||
constexpr auto value = stripped_type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] stl::string_view type_name(char) noexcept {
|
||||
static const auto value = stripped_type_name<Type>();
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type, auto = stripped_type_name<Type>().find_first_of('.')>
|
||||
[[nodiscard]] ENTT_CONSTEVAL id_type type_hash(int) noexcept {
|
||||
constexpr auto stripped = stripped_type_name<Type>();
|
||||
constexpr auto value = hashed_string::value(stripped.data(), stripped.size());
|
||||
return value;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[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<Type>());
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Type sequential identifier.
|
||||
* @tparam Type Type for which to generate a sequential identifier.
|
||||
*/
|
||||
template<typename Type>
|
||||
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<typename Type>
|
||||
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<Type>(0);
|
||||
#else
|
||||
[[nodiscard]] static constexpr id_type value() noexcept {
|
||||
return type_index<Type>::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<typename Type>
|
||||
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<Type>(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<typename Type>
|
||||
// NOLINTBEGIN(modernize-use-transparent-functors)
|
||||
constexpr type_info(stl::in_place_type_t<Type>) noexcept
|
||||
: seq{type_index<stl::remove_cvref_t<Type>>::value()},
|
||||
identifier{type_hash<stl::remove_cvref_t<Type>>::value()},
|
||||
alias{type_name<stl::remove_cvref_t<Type>>::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.<br/>
|
||||
* The type doesn't need to be a complete type. If the type is a reference, the
|
||||
* result refers to the referenced type. In all cases, top-level cv-qualifiers
|
||||
* are ignored.
|
||||
*
|
||||
* @tparam Type Type for which to generate a type info object.
|
||||
* @return A reference to a properly initialized type info object.
|
||||
*/
|
||||
template<typename Type>
|
||||
[[nodiscard]] const type_info &type_id() noexcept {
|
||||
if constexpr(stl::is_same_v<Type, stl::remove_cvref_t<Type>>) {
|
||||
static const type_info instance{stl::in_place_type<Type>};
|
||||
return instance;
|
||||
} else {
|
||||
return type_id<stl::remove_cvref_t<Type>>();
|
||||
}
|
||||
}
|
||||
|
||||
/*! @copydoc type_id */
|
||||
template<typename Type>
|
||||
[[nodiscard]] const type_info &type_id(const Type &) noexcept {
|
||||
return type_id<stl::remove_cvref_t<Type>>();
|
||||
}
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
909
include/entt/core/type_traits.hpp
Normal file
909
include/entt/core/type_traits.hpp
Normal file
@@ -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<stl::size_t N>
|
||||
struct choice_t
|
||||
// unfortunately, doxygen cannot parse such a construct
|
||||
: /*! @cond ENTT_INTERNAL */ choice_t<N - 1> /*! @endcond */
|
||||
{};
|
||||
|
||||
/*! @copybrief choice_t */
|
||||
template<>
|
||||
struct choice_t<0> {};
|
||||
|
||||
/**
|
||||
* @brief Variable template for the choice trick.
|
||||
* @tparam N Number of choices available.
|
||||
*/
|
||||
template<stl::size_t N>
|
||||
inline constexpr choice_t<N> choice{};
|
||||
|
||||
/**
|
||||
* @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains.
|
||||
* @tparam Type The type of which to return the size.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct size_of: stl::integral_constant<stl::size_t, 0u> {};
|
||||
|
||||
/*! @copydoc size_of */
|
||||
template<typename Type>
|
||||
requires requires { sizeof(Type); }
|
||||
struct size_of<Type>
|
||||
// NOLINTNEXTLINE(bugprone-sizeof-expression)
|
||||
: stl::integral_constant<stl::size_t, sizeof(Type)> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type of which to return the size.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr stl::size_t size_of_v = size_of<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Using declaration to be used to _repeat_ the same type a number of
|
||||
* times equal to the size of a given parameter pack.
|
||||
* @tparam Type A type to repeat.
|
||||
*/
|
||||
template<typename Type, typename>
|
||||
using unpack_as_type = Type;
|
||||
|
||||
/**
|
||||
* @brief Helper variable template to be used to _repeat_ the same value a
|
||||
* number of times equal to the size of a given parameter pack.
|
||||
* @tparam Value A value to repeat.
|
||||
*/
|
||||
template<auto Value, typename>
|
||||
inline constexpr auto unpack_as_value = Value;
|
||||
|
||||
/**
|
||||
* @brief Wraps a static constant.
|
||||
* @tparam Value A static constant.
|
||||
*/
|
||||
template<auto Value>
|
||||
using integral_constant = stl::integral_constant<decltype(Value), Value>;
|
||||
|
||||
/**
|
||||
* @brief Alias template to facilitate the creation of named values.
|
||||
* @tparam Value A constant value at least convertible to `id_type`.
|
||||
*/
|
||||
template<id_type Value>
|
||||
using tag = integral_constant<Value>;
|
||||
|
||||
/**
|
||||
* @brief A class to use to push around lists of types, nothing more.
|
||||
* @tparam Type Types provided by the type list.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct type_list {
|
||||
/*! @brief Type list type. */
|
||||
using type = type_list;
|
||||
/*! @brief Compile-time number of elements in the type list. */
|
||||
static constexpr auto size = sizeof...(Type);
|
||||
};
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<stl::size_t, typename>
|
||||
struct type_list_element;
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam Index Index of the type to return.
|
||||
* @tparam First First type provided by the type list.
|
||||
* @tparam Other Other types provided by the type list.
|
||||
*/
|
||||
template<stl::size_t Index, typename First, typename... Other>
|
||||
struct type_list_element<Index, type_list<First, Other...>>
|
||||
: type_list_element<Index - 1u, type_list<Other...>> {};
|
||||
|
||||
/**
|
||||
* @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<typename First, typename... Other>
|
||||
struct type_list_element<0u, type_list<First, Other...>> {
|
||||
/*! @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<stl::size_t Index, typename List>
|
||||
using type_list_element_t = type_list_element<Index, List>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename, typename>
|
||||
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<typename Type, typename First, typename... Other>
|
||||
struct type_list_index<Type, type_list<First, Other...>> {
|
||||
/*! @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<Type, type_list<Other...>>::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<typename Type, typename... Other>
|
||||
requires (type_list_index<Type, type_list<Other...>>::value == sizeof...(Other))
|
||||
struct type_list_index<Type, type_list<Type, Other...>> {
|
||||
/*! @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<typename Type>
|
||||
struct type_list_index<Type, type_list<>> {
|
||||
/*! @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<typename Type, typename List>
|
||||
inline constexpr stl::size_t type_list_index_v = type_list_index<Type, List>::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<typename... Type, typename... Other>
|
||||
ENTT_CONSTEVAL type_list<Type..., Other...> operator+(type_list<Type...>, type_list<Other...>) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct type_list_cat;
|
||||
|
||||
/*! @brief Concatenates multiple type lists. */
|
||||
template<>
|
||||
struct type_list_cat<> {
|
||||
/*! @brief A type list composed by the types of all the type lists. */
|
||||
using type = type_list<>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
* @tparam Other Types provided by the second type list.
|
||||
* @tparam List Other type lists, if any.
|
||||
*/
|
||||
template<typename... Type, typename... Other, typename... List>
|
||||
struct type_list_cat<type_list<Type...>, type_list<Other...>, List...> {
|
||||
/*! @brief A type list composed by the types of all the type lists. */
|
||||
using type = type_list_cat<type_list<Type..., Other...>, List...>::type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple type lists.
|
||||
* @tparam Type Types provided by the type list.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct type_list_cat<type_list<Type...>> {
|
||||
/*! @brief A type list composed by the types of all the type lists. */
|
||||
using type = type_list<Type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type lists to concatenate.
|
||||
*/
|
||||
template<typename... List>
|
||||
using type_list_cat_t = type_list_cat<List...>::type;
|
||||
|
||||
/*! @cond ENTT_INTERNAL */
|
||||
namespace internal {
|
||||
|
||||
template<typename...>
|
||||
struct type_list_unique;
|
||||
|
||||
template<typename First, typename... Other, typename... Type>
|
||||
struct type_list_unique<type_list<First, Other...>, Type...>
|
||||
: stl::conditional_t<(stl::is_same_v<First, Type> || ...), type_list_unique<type_list<Other...>, Type...>, type_list_unique<type_list<Other...>, Type..., First>> {};
|
||||
|
||||
template<typename... Type>
|
||||
struct type_list_unique<type_list<>, Type...> {
|
||||
using type = type_list<Type...>;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Removes duplicates types from a type list.
|
||||
* @tparam List Type list.
|
||||
*/
|
||||
template<typename List>
|
||||
struct type_list_unique {
|
||||
/*! @brief A type list without duplicate types. */
|
||||
using type = internal::type_list_unique<List>::type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type list.
|
||||
*/
|
||||
template<typename List>
|
||||
using type_list_unique_t = type_list_unique<List>::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<typename List, typename Type>
|
||||
struct type_list_contains;
|
||||
|
||||
/**
|
||||
* @copybrief type_list_contains
|
||||
* @tparam Type Types provided by the type list.
|
||||
* @tparam Other Type to look for.
|
||||
*/
|
||||
template<typename... Type, typename Other>
|
||||
struct type_list_contains<type_list<Type...>, Other>
|
||||
: stl::bool_constant<(stl::is_same_v<Type, Other> || ...)> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Type list.
|
||||
* @tparam Type Type to look for.
|
||||
*/
|
||||
template<typename List, typename Type>
|
||||
inline constexpr bool type_list_contains_v = type_list_contains<List, Type>::value;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct type_list_diff;
|
||||
|
||||
/**
|
||||
* @brief Computes the difference between two type lists.
|
||||
* @tparam Type Types provided by the first type list.
|
||||
* @tparam Other Types provided by the second type list.
|
||||
*/
|
||||
template<typename... Type, typename... Other>
|
||||
struct type_list_diff<type_list<Type...>, type_list<Other...>> {
|
||||
/*! @brief A type list that is the difference between the two type lists. */
|
||||
using type = type_list_cat_t<stl::conditional_t<type_list_contains_v<type_list<Other...>, Type>, type_list<>, type_list<Type>>...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type lists between which to compute the difference.
|
||||
*/
|
||||
template<typename... List>
|
||||
using type_list_diff_t = type_list_diff<List...>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename, template<typename...> 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<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<type_list<Type...>, Op> {
|
||||
/*! @brief Resulting type list after applying the transform function. */
|
||||
// NOLINTNEXTLINE(modernize-type-traits)
|
||||
using type = type_list<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Type list.
|
||||
* @tparam Op Unary operation as template class with a type member named `type`.
|
||||
*/
|
||||
template<typename List, template<typename...> class Op>
|
||||
using type_list_transform_t = type_list_transform<List, Op>::type;
|
||||
|
||||
/**
|
||||
* @brief A class to use to push around lists of constant values, nothing more.
|
||||
* @tparam Value Values provided by the value list.
|
||||
*/
|
||||
template<auto... Value>
|
||||
struct value_list {
|
||||
/*! @brief Value list type. */
|
||||
using type = value_list;
|
||||
/*! @brief Compile-time number of elements in the value list. */
|
||||
static constexpr auto size = sizeof...(Value);
|
||||
};
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<stl::size_t, typename>
|
||||
struct value_list_element;
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the values of a value list.
|
||||
* @tparam Index Index of the value to return.
|
||||
* @tparam Value First value provided by the value list.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<stl::size_t Index, auto Value, auto... Other>
|
||||
struct value_list_element<Index, value_list<Value, Other...>>
|
||||
: value_list_element<Index - 1u, value_list<Other...>> {};
|
||||
|
||||
/**
|
||||
* @brief Provides compile-time indexed access to the types of a type list.
|
||||
* @tparam Value First value provided by the value list.
|
||||
* @tparam Other Other values provided by the value list.
|
||||
*/
|
||||
template<auto Value, auto... Other>
|
||||
struct value_list_element<0u, value_list<Value, Other...>> {
|
||||
/*! @brief Searched 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<stl::size_t Index, typename List>
|
||||
using value_list_element_t = value_list_element<Index, List>::type;
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index Index of the value to return.
|
||||
* @tparam List Value list to search into.
|
||||
*/
|
||||
template<stl::size_t Index, typename List>
|
||||
inline constexpr auto value_list_element_v = value_list_element<Index, List>::value;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<auto, typename>
|
||||
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<auto Value, auto First, auto... Other>
|
||||
struct value_list_index<Value, value_list<First, Other...>> {
|
||||
/*! @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, value_list<Other...>>::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<auto Value, auto... Other>
|
||||
requires (value_list_index<Value, value_list<Other...>>::value == sizeof...(Other))
|
||||
struct value_list_index<Value, value_list<Value, Other...>> {
|
||||
/*! @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<auto Value>
|
||||
struct value_list_index<Value, value_list<>> {
|
||||
/*! @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<auto Value, typename List>
|
||||
inline constexpr stl::size_t value_list_index_v = value_list_index<Value, List>::value;
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
* @tparam Other Values provided by the second value list.
|
||||
* @return A value list composed by the values of both the value lists.
|
||||
*/
|
||||
template<auto... Value, auto... Other>
|
||||
ENTT_CONSTEVAL value_list<Value..., Other...> operator+(value_list<Value...>, value_list<Other...>) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct value_list_cat;
|
||||
|
||||
/*! @brief Concatenates multiple value lists. */
|
||||
template<>
|
||||
struct value_list_cat<> {
|
||||
/*! @brief A value list composed by the values of all the value lists. */
|
||||
using type = value_list<>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the first value list.
|
||||
* @tparam Other Values provided by the second value list.
|
||||
* @tparam List Other value lists, if any.
|
||||
*/
|
||||
template<auto... Value, auto... Other, typename... List>
|
||||
struct value_list_cat<value_list<Value...>, value_list<Other...>, List...> {
|
||||
/*! @brief A value list composed by the values of all the value lists. */
|
||||
using type = value_list_cat<value_list<Value..., Other...>, List...>::type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Concatenates multiple value lists.
|
||||
* @tparam Value Values provided by the value list.
|
||||
*/
|
||||
template<auto... Value>
|
||||
struct value_list_cat<value_list<Value...>> {
|
||||
/*! @brief A value list composed by the values of all the value lists. */
|
||||
using type = value_list<Value...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Value lists to concatenate.
|
||||
*/
|
||||
template<typename... List>
|
||||
using value_list_cat_t = value_list_cat<List...>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename>
|
||||
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<auto Value, auto... Other>
|
||||
struct value_list_unique<value_list<Value, Other...>> {
|
||||
/*! @brief A value list without duplicate types. */
|
||||
using type = stl::conditional_t<
|
||||
((Value == Other) || ...),
|
||||
typename value_list_unique<value_list<Other...>>::type,
|
||||
value_list_cat_t<value_list<Value>, typename value_list_unique<value_list<Other...>>::type>>;
|
||||
};
|
||||
|
||||
/*! @brief Removes duplicates values from a value list. */
|
||||
template<>
|
||||
struct value_list_unique<value_list<>> {
|
||||
/*! @brief A value list without duplicate types. */
|
||||
using type = value_list<>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Type A value list.
|
||||
*/
|
||||
template<typename Type>
|
||||
using value_list_unique_t = value_list_unique<Type>::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<typename List, auto Value>
|
||||
struct value_list_contains;
|
||||
|
||||
/**
|
||||
* @copybrief value_list_contains
|
||||
* @tparam Value Values provided by the value list.
|
||||
* @tparam Other Value to look for.
|
||||
*/
|
||||
template<auto... Value, auto Other>
|
||||
struct value_list_contains<value_list<Value...>, Other>
|
||||
: stl::bool_constant<((Value == Other) || ...)> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam List Value list.
|
||||
* @tparam Value Value to look for.
|
||||
*/
|
||||
template<typename List, auto Value>
|
||||
inline constexpr bool value_list_contains_v = value_list_contains<List, Value>::value;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
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<auto... Value, auto... Other>
|
||||
struct value_list_diff<value_list<Value...>, value_list<Other...>> {
|
||||
/*! @brief A value list that is the difference between the two lists. */
|
||||
using type = value_list_cat_t<stl::conditional_t<value_list_contains_v<value_list<Other...>, Value>, value_list<>, value_list<Value>>...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam List Value lists between which to compute the difference.
|
||||
*/
|
||||
template<typename... List>
|
||||
using value_list_diff_t = value_list_diff<List...>::type;
|
||||
|
||||
/*! @brief Same as stl::is_invocable, but with tuples. */
|
||||
template<typename, typename>
|
||||
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<typename Func, template<typename...> class Tuple, typename... Args>
|
||||
struct is_applicable<Func, Tuple<Args...>>: stl::is_invocable<Func, Args...> {};
|
||||
|
||||
/**
|
||||
* @copybrief is_applicable
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Tuple Tuple-like type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Func, template<typename...> class Tuple, typename... Args>
|
||||
struct is_applicable<Func, const Tuple<Args...>>: stl::is_invocable<Func, Args...> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Func, typename Args>
|
||||
inline constexpr bool is_applicable_v = is_applicable<Func, Args>::value;
|
||||
|
||||
/*! @brief Same as stl::is_invocable_r, but with tuples for arguments. */
|
||||
template<typename, typename, typename>
|
||||
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<typename Ret, typename Func, typename... Args>
|
||||
struct is_applicable_r<Ret, Func, stl::tuple<Args...>>: stl::is_invocable_r<Ret, Func, Args...> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Ret The type to which the return type of the function should be
|
||||
* convertible.
|
||||
* @tparam Func A valid function type.
|
||||
* @tparam Args The list of arguments to use to probe the function type.
|
||||
*/
|
||||
template<typename Ret, typename Func, typename Args>
|
||||
inline constexpr bool is_applicable_r_v = is_applicable_r<Ret, Func, Args>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` equal to true if a given type is
|
||||
* complete, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_complete: stl::false_type {};
|
||||
|
||||
/*! @copydoc is_complete */
|
||||
template<typename Type>
|
||||
requires requires { sizeof(Type); }
|
||||
struct is_complete<Type>: stl::true_type {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_complete_v = is_complete<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` equal to true if a given type is
|
||||
* an iterator, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_iterator: stl::false_type {};
|
||||
|
||||
/*! @cond ENTT_INTERNAL */
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
struct has_iterator_category: stl::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
requires requires { typename stl::iterator_traits<Type>::iterator_category; }
|
||||
struct has_iterator_category<Type>: stl::true_type {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/*! @copydoc is_iterator */
|
||||
template<typename Type>
|
||||
requires (!stl::is_void_v<stl::remove_const_t<stl::remove_pointer_t<Type>>>)
|
||||
struct is_iterator<Type>: internal::has_iterator_category<Type> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_iterator_v = is_iterator<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` equal to true if a given type is
|
||||
* both an empty and non-final class, false otherwise.
|
||||
* @tparam Type The type to test
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_ebco_eligible: stl::bool_constant<stl::is_empty_v<Type> && !stl::is_final_v<Type>> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_ebco_eligible_v = is_ebco_eligible<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Provides the member constant `value` equal to true if
|
||||
* `Type::is_transparent` is valid and denotes a type, false otherwise.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
struct is_transparent: stl::false_type {};
|
||||
|
||||
/*! @copydoc is_transparent */
|
||||
template<typename Type>
|
||||
requires requires { typename Type::is_transparent; }
|
||||
struct is_transparent<Type>: stl::true_type {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_transparent_v = is_transparent<Type>::value;
|
||||
|
||||
/*! @cond ENTT_INTERNAL */
|
||||
namespace internal {
|
||||
|
||||
template<typename>
|
||||
struct has_tuple_size_value: stl::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
requires is_complete_v<stl::tuple_size<const Type>>
|
||||
struct has_tuple_size_value<Type>: stl::true_type {};
|
||||
|
||||
template<typename>
|
||||
struct has_value_type: stl::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
requires requires { typename Type::value_type; }
|
||||
struct has_value_type<Type>: stl::true_type {};
|
||||
|
||||
template<typename>
|
||||
[[nodiscard]] ENTT_CONSTEVAL bool dispatch_is_equality_comparable();
|
||||
|
||||
template<typename Type, stl::size_t... Index>
|
||||
[[nodiscard]] ENTT_CONSTEVAL bool unpack_maybe_equality_comparable(stl::index_sequence<Index...>) {
|
||||
return (dispatch_is_equality_comparable<stl::tuple_element_t<Index, Type>>() && ...);
|
||||
}
|
||||
|
||||
template<typename>
|
||||
[[nodiscard]] ENTT_CONSTEVAL bool maybe_equality_comparable(char) {
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] ENTT_CONSTEVAL auto maybe_equality_comparable(int) -> decltype(stl::declval<Type>() == stl::declval<Type>()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Type>
|
||||
[[nodiscard]] ENTT_CONSTEVAL bool dispatch_is_equality_comparable() {
|
||||
// NOLINTBEGIN(modernize-use-transparent-functors)
|
||||
if constexpr(stl::is_array_v<Type>) {
|
||||
return false;
|
||||
} else if constexpr(is_complete_v<stl::tuple_size<stl::remove_const_t<Type>>>) {
|
||||
if constexpr(has_tuple_size_value<Type>::value) {
|
||||
return maybe_equality_comparable<Type>(0) && unpack_maybe_equality_comparable<Type>(stl::make_index_sequence<stl::tuple_size<Type>::value>{});
|
||||
} else {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
}
|
||||
} else if constexpr(has_value_type<Type>::value) {
|
||||
if constexpr(is_iterator_v<Type> || stl::is_same_v<typename Type::value_type, Type> || dispatch_is_equality_comparable<typename Type::value_type>()) {
|
||||
return maybe_equality_comparable<Type>(0);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return maybe_equality_comparable<Type>(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<typename Type>
|
||||
struct is_equality_comparable: stl::bool_constant<internal::dispatch_is_equality_comparable<Type>()> {};
|
||||
|
||||
/*! @copydoc is_equality_comparable */
|
||||
template<typename Type>
|
||||
struct is_equality_comparable<const Type>: is_equality_comparable<Type> {};
|
||||
|
||||
/**
|
||||
* @brief Helper variable template.
|
||||
* @tparam Type The type to test.
|
||||
*/
|
||||
template<typename Type>
|
||||
inline constexpr bool is_equality_comparable_v = is_equality_comparable<Type>::value;
|
||||
|
||||
/**
|
||||
* @brief Transcribes the constness of a type to another type.
|
||||
* @tparam To The type to which to transcribe the constness.
|
||||
* @tparam From The type from which to transcribe the constness.
|
||||
*/
|
||||
template<typename To, typename From>
|
||||
struct constness_as {
|
||||
/*! @brief The type resulting from the transcription of the constness. */
|
||||
using type = stl::remove_const_t<To>;
|
||||
};
|
||||
|
||||
/*! @copydoc constness_as */
|
||||
template<typename To, typename From>
|
||||
struct constness_as<To, const From> {
|
||||
/*! @brief The type resulting from the transcription of the constness. */
|
||||
using type = 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<typename To, typename From>
|
||||
using constness_as_t = constness_as<To, From>::type;
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename>
|
||||
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<typename Member>
|
||||
requires stl::is_member_pointer_v<Member>
|
||||
class member_class<Member> {
|
||||
template<typename Class, typename Ret, typename... Args>
|
||||
static Class *clazz(Ret (Class::*)(Args...));
|
||||
|
||||
template<typename Class, typename Ret, typename... Args>
|
||||
static Class *clazz(Ret (Class::*)(Args...) const);
|
||||
|
||||
template<typename Class, typename Type>
|
||||
static Class *clazz(Type Class::*);
|
||||
|
||||
public:
|
||||
/*! @brief The class of the given non-static member object or function. */
|
||||
using type = stl::remove_pointer_t<decltype(clazz(stl::declval<Member>()))>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Member A pointer to a non-static member object or function.
|
||||
*/
|
||||
template<typename Member>
|
||||
using member_class_t = member_class<Member>::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<stl::size_t Index, typename Candidate>
|
||||
class nth_argument {
|
||||
template<typename Ret, typename... Args>
|
||||
static ENTT_CONSTEVAL type_list<Args...> pick_up(Ret (*)(Args...));
|
||||
|
||||
template<typename Ret, typename Class, typename... Args>
|
||||
static ENTT_CONSTEVAL type_list<Args...> pick_up(Ret (Class ::*)(Args...));
|
||||
|
||||
template<typename Ret, typename Class, typename... Args>
|
||||
static ENTT_CONSTEVAL type_list<Args...> pick_up(Ret (Class ::*)(Args...) const);
|
||||
|
||||
template<typename Type, typename Class>
|
||||
static ENTT_CONSTEVAL type_list<Type> pick_up(Type Class ::*);
|
||||
|
||||
template<typename Type>
|
||||
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<Index, decltype(pick_up(stl::declval<Candidate>()))>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Index The index of the argument to extract.
|
||||
* @tparam Candidate A valid function, member function or data member type.
|
||||
*/
|
||||
template<stl::size_t Index, typename Candidate>
|
||||
using nth_argument_t = nth_argument<Index, Candidate>::type;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
template<typename... Type>
|
||||
struct entt::stl::tuple_size<entt::type_list<Type...>>: entt::stl::integral_constant<entt::stl::size_t, entt::type_list<Type...>::size> {};
|
||||
|
||||
template<entt::stl::size_t Index, typename... Type>
|
||||
struct entt::stl::tuple_element<Index, entt::type_list<Type...>>: entt::type_list_element<Index, entt::type_list<Type...>> {};
|
||||
|
||||
template<auto... Value>
|
||||
struct entt::stl::tuple_size<entt::value_list<Value...>>: entt::stl::integral_constant<entt::stl::size_t, entt::value_list<Value...>::size> {};
|
||||
|
||||
template<entt::stl::size_t Index, auto... Value>
|
||||
struct entt::stl::tuple_element<Index, entt::value_list<Value...>>: entt::value_list_element<Index, entt::value_list<Value...>> {};
|
||||
|
||||
#endif
|
||||
84
include/entt/core/utility.hpp
Normal file
84
include/entt/core/utility.hpp
Normal file
@@ -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<typename Type, typename Class>
|
||||
[[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<typename Func>
|
||||
[[nodiscard]] constexpr auto overload(Func *func) noexcept {
|
||||
return func;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper type for visitors.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<typename... Func>
|
||||
struct overloaded: Func... {
|
||||
using Func::operator()...;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Func Types of function objects.
|
||||
*/
|
||||
template<typename... Func>
|
||||
overloaded(Func...) -> overloaded<Func...>;
|
||||
|
||||
/**
|
||||
* @brief Basic implementation of a y-combinator.
|
||||
* @tparam Func Type of a potentially recursive function.
|
||||
*/
|
||||
template<typename Func>
|
||||
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>)
|
||||
: 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<typename... Args>
|
||||
constexpr decltype(auto) operator()(Args &&...args) const noexcept(stl::is_nothrow_invocable_v<Func, const y_combinator &, Args...>) {
|
||||
return func(*this, stl::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/*! @copydoc operator()() */
|
||||
template<typename... Args>
|
||||
constexpr decltype(auto) operator()(Args &&...args) noexcept(stl::is_nothrow_invocable_v<Func, y_combinator &, Args...>) {
|
||||
return func(*this, stl::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Func func;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
59
include/entt/entity/component.hpp
Normal file
59
include/entt/entity/component.hpp
Normal file
@@ -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<typename Type>
|
||||
struct in_place_delete: stl::bool_constant<!(stl::is_move_constructible_v<Type> && stl::is_move_assignable_v<Type>)> {};
|
||||
|
||||
template<>
|
||||
struct in_place_delete<void>: stl::false_type {};
|
||||
|
||||
template<typename Type>
|
||||
requires Type::in_place_delete
|
||||
struct in_place_delete<Type>: stl::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
struct page_size: stl::integral_constant<stl::size_t, !stl::is_empty_v<ENTT_ETO_TYPE(Type)> * ENTT_PACKED_PAGE> {};
|
||||
|
||||
template<>
|
||||
struct page_size<void>: stl::integral_constant<stl::size_t, 0u> {};
|
||||
|
||||
template<typename Type>
|
||||
requires stl::is_convertible_v<decltype(Type::page_size), stl::size_t>
|
||||
struct page_size<Type>: stl::integral_constant<stl::size_t, Type::page_size> {};
|
||||
|
||||
} // namespace internal
|
||||
/*! @endcond */
|
||||
|
||||
/**
|
||||
* @brief Common way to access various properties of components.
|
||||
* @tparam Type Element type.
|
||||
* @tparam Entity A valid entity type.
|
||||
*/
|
||||
template<cvref_unqualified Type, typename Entity>
|
||||
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<Type>::value;
|
||||
/*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */
|
||||
static constexpr stl::size_t page_size = internal::page_size<Type>::value;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
312
include/entt/entity/entity.hpp
Normal file
312
include/entt/entity/entity.hpp
Normal file
@@ -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<typename>
|
||||
struct entt_traits;
|
||||
|
||||
template<typename Type>
|
||||
requires requires {
|
||||
requires stl::is_enum_v<Type>;
|
||||
typename internal::entt_traits<stl::underlying_type_t<Type>>::value_type;
|
||||
}
|
||||
struct entt_traits<Type>: entt_traits<stl::underlying_type_t<Type>> {
|
||||
using value_type = Type;
|
||||
};
|
||||
|
||||
template<typename Type>
|
||||
requires requires { typename Type::entity_type; }
|
||||
struct entt_traits<Type>
|
||||
: entt_traits<typename Type::entity_type> {
|
||||
using value_type = Type;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct entt_traits<stl::uint32_t> {
|
||||
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<stl::uint64_t> {
|
||||
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<typename Type>
|
||||
concept entity_like = requires {
|
||||
typename internal::entt_traits<Type>::value_type;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Common basic entity traits implementation.
|
||||
* @tparam Traits Actual entity traits to use.
|
||||
*/
|
||||
template<typename Traits>
|
||||
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<entity_type>(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity part once converted to the underlying type.
|
||||
* @param value The value to convert.
|
||||
* @return The integral representation of the entity part.
|
||||
*/
|
||||
[[nodiscard]] static constexpr entity_type to_entity(const value_type value) 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<version_type>(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<version_type>(vers + (vers == version_mask)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructs an identifier from its parts.
|
||||
*
|
||||
* If the version part is not provided, a tombstone is returned.<br/>
|
||||
* If the entity part is not provided, a null identifier is returned.
|
||||
*
|
||||
* @param entity The entity part of the identifier.
|
||||
* @param version The version part of the identifier.
|
||||
* @return A properly constructed identifier.
|
||||
*/
|
||||
[[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept {
|
||||
if constexpr(Traits::version_mask == 0u) {
|
||||
return value_type{entity & entity_mask};
|
||||
} else {
|
||||
return value_type{(entity & entity_mask) | (static_cast<entity_type>(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<entity_like Type>
|
||||
struct entt_traits: basic_entt_traits<internal::entt_traits<Type>> {
|
||||
/*! @brief Base type. */
|
||||
using base_type = basic_entt_traits<internal::entt_traits<Type>>;
|
||||
/*! @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<typename Entity>
|
||||
[[nodiscard]] constexpr entt_traits<Entity>::entity_type to_integral(const Entity value) noexcept {
|
||||
return entt_traits<Entity>::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<typename Entity>
|
||||
[[nodiscard]] constexpr entt_traits<Entity>::entity_type to_entity(const Entity value) noexcept {
|
||||
return entt_traits<Entity>::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<typename Entity>
|
||||
[[nodiscard]] constexpr entt_traits<Entity>::version_type to_version(const Entity value) noexcept {
|
||||
return entt_traits<Entity>::to_version(value);
|
||||
}
|
||||
|
||||
/*! @brief Null object for all identifiers. */
|
||||
struct null_t {
|
||||
/**
|
||||
* @brief Converts the null object to identifiers of any type.
|
||||
* @tparam Entity Type of identifier.
|
||||
* @return The null representation for the given type.
|
||||
*/
|
||||
template<entity_like Entity>
|
||||
[[nodiscard]] constexpr operator Entity() const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
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<entity_like Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
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<entity_like Entity>
|
||||
[[nodiscard]] constexpr operator Entity() const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
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<entity_like Entity>
|
||||
[[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept {
|
||||
using traits_type = entt_traits<Entity>;
|
||||
|
||||
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
|
||||
291
include/entt/entity/fwd.hpp
Normal file
291
include/entt/entity/fwd.hpp
Normal file
@@ -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<cvref_unqualified Type, typename Entity = entity>
|
||||
struct component_traits;
|
||||
|
||||
template<typename Entity = entity, typename = stl::allocator<Entity>>
|
||||
class basic_sparse_set;
|
||||
|
||||
template<typename Type, typename = entity, typename = stl::allocator<Type>>
|
||||
class basic_storage;
|
||||
|
||||
template<typename, typename>
|
||||
class basic_sigh_mixin;
|
||||
|
||||
template<typename, typename>
|
||||
class basic_reactive_mixin;
|
||||
|
||||
template<typename Entity = entity, typename = stl::allocator<Entity>>
|
||||
class basic_registry;
|
||||
|
||||
template<typename, typename>
|
||||
class basic_view;
|
||||
|
||||
template<typename Type, typename = stl::allocator<Type *>>
|
||||
class basic_runtime_view;
|
||||
|
||||
template<typename, typename, typename>
|
||||
class basic_group;
|
||||
|
||||
template<typename>
|
||||
class basic_organizer;
|
||||
|
||||
template<typename, typename...>
|
||||
class basic_handle;
|
||||
|
||||
template<typename>
|
||||
class basic_snapshot;
|
||||
|
||||
template<typename>
|
||||
class basic_snapshot_loader;
|
||||
|
||||
template<typename>
|
||||
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<typename Type>
|
||||
using storage = basic_storage<Type>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Underlying storage type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using sigh_mixin = basic_sigh_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Type Underlying storage type.
|
||||
*/
|
||||
template<typename Type>
|
||||
using reactive_mixin = basic_reactive_mixin<Type, basic_registry<typename Type::entity_type, typename Type::base_type::allocator_type>>;
|
||||
|
||||
/*! @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<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using handle = basic_handle<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_handle = basic_handle<const registry>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using handle_view = basic_handle<registry, Args...>;
|
||||
|
||||
/**
|
||||
* @brief Alias declaration for the most common use case.
|
||||
* @tparam Args Other template parameters.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using const_handle_view = basic_handle<const registry, Args...>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot = basic_snapshot<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using snapshot_loader = basic_snapshot_loader<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using continuous_loader = basic_continuous_loader<registry>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using runtime_view = basic_runtime_view<sparse_set>;
|
||||
|
||||
/*! @brief Alias declaration for the most common use case. */
|
||||
using const_runtime_view = basic_runtime_view<const sparse_set>;
|
||||
|
||||
/**
|
||||
* @brief Alias for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct exclude_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit ENTT_CONSTEVAL exclude_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for exclusion lists.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr exclude_t<Type...> exclude{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of observed elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct get_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit ENTT_CONSTEVAL get_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of observed elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr get_t<Type...> get{};
|
||||
|
||||
/**
|
||||
* @brief Alias for lists of owned elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
struct owned_t final: type_list<Type...> {
|
||||
/*! @brief Default constructor. */
|
||||
explicit ENTT_CONSTEVAL owned_t() = default;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Variable template for lists of owned elements.
|
||||
* @tparam Type List of types.
|
||||
*/
|
||||
template<typename... Type>
|
||||
inline constexpr owned_t<Type...> 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<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<get_t<Type...>, Op> {
|
||||
/*! @brief Resulting get list after applying the transform function. */
|
||||
using type = get_t<typename Op<Type>::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<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<exclude_t<Type...>, Op> {
|
||||
/*! @brief Resulting exclude list after applying the transform function. */
|
||||
using type = exclude_t<typename Op<Type>::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<typename... Type, template<typename...> class Op>
|
||||
struct type_list_transform<owned_t<Type...>, Op> {
|
||||
/*! @brief Resulting owned list after applying the transform function. */
|
||||
using type = owned_t<typename Op<Type>::type...>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Provides a common way to define storage types.
|
||||
* @tparam Type Storage value type.
|
||||
* @tparam Entity A valid entity type.
|
||||
* @tparam Allocator Type of allocator used to manage memory and elements.
|
||||
*/
|
||||
template<typename Type, typename Entity = entity, typename Allocator = stl::allocator<Type>>
|
||||
struct storage_type {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = ENTT_STORAGE(sigh_mixin, basic_storage<Type, Entity, Allocator>);
|
||||
};
|
||||
|
||||
/*! @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<typename Entity, typename Allocator>
|
||||
struct storage_type<reactive, Entity, Allocator> {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = ENTT_STORAGE(reactive_mixin, basic_storage<reactive, Entity, Allocator>);
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Args Arguments to forward.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_type_t = storage_type<Args...>::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<typename Type, typename Entity = entity, typename Allocator = stl::allocator<stl::remove_const_t<Type>>>
|
||||
struct storage_for {
|
||||
/*! @brief Type-to-storage conversion result. */
|
||||
using type = constness_as_t<storage_type_t<stl::remove_const_t<Type>, Entity, Allocator>, Type>;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper type.
|
||||
* @tparam Args Arguments to forward.
|
||||
*/
|
||||
template<typename... Args>
|
||||
using storage_for_t = storage_for<Args...>::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<typename Get, typename Exclude = exclude_t<>>
|
||||
using view = basic_view<type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
|
||||
|
||||
/**
|
||||
* @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 Owned, typename Get = get_t<>, typename Exclude = exclude_t<>>
|
||||
using group = basic_group<type_list_transform_t<Owned, storage_for>, type_list_transform_t<Get, storage_for>, type_list_transform_t<Exclude, storage_for>>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
1052
include/entt/entity/group.hpp
Normal file
1052
include/entt/entity/group.hpp
Normal file
File diff suppressed because it is too large
Load Diff
368
include/entt/entity/handle.hpp
Normal file
368
include/entt/entity/handle.hpp
Normal file
@@ -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<typename It>
|
||||
class handle_storage_iterator final {
|
||||
template<typename>
|
||||
friend class handle_storage_iterator;
|
||||
|
||||
using underlying_type = stl::remove_reference_t<typename It::value_type::second_type>;
|
||||
using entity_type = underlying_type::entity_type;
|
||||
|
||||
public:
|
||||
using value_type = stl::iterator_traits<It>::value_type;
|
||||
using pointer = input_iterator_pointer<value_type>;
|
||||
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<typename Other>
|
||||
[[nodiscard]] constexpr bool operator==(const handle_storage_iterator<Other> &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<typename Registry, typename... Scope>
|
||||
class basic_handle {
|
||||
using traits_type = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
[[nodiscard]] auto &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<Registry &>(*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<internal::handle_storage_iterator<typename decltype(stl::declval<registry_type>().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.<br/>
|
||||
* 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<bool>(*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<typename Type, typename... Args>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
decltype(auto) emplace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || stl::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template emplace<Type>(entt, stl::forward<Args>(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<typename Type, typename... Args>
|
||||
decltype(auto) emplace_or_replace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || stl::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template emplace_or_replace<Type>(entt, stl::forward<Args>(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<typename Type, typename... Func>
|
||||
decltype(auto) patch(Func &&...func) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || stl::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template patch<Type>(entt, stl::forward<Func>(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<typename Type, typename... Args>
|
||||
decltype(auto) replace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || stl::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template replace<Type>(entt, stl::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the given elements from a handle.
|
||||
* @tparam Type Types of elements to remove.
|
||||
* @return The number of elements actually removed.
|
||||
*/
|
||||
template<typename... Type>
|
||||
// NOLINTNEXTLINE(modernize-use-nodiscard)
|
||||
size_type remove() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
return owner_or_assert().template remove<Type...>(entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Erases the given elements from a handle.
|
||||
* @tparam Type Types of elements to erase.
|
||||
*/
|
||||
template<typename... Type>
|
||||
void erase() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
owner_or_assert().template erase<Type...>(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<typename... Type>
|
||||
[[nodiscard]] decltype(auto) all_of() const {
|
||||
return owner_or_assert().template all_of<Type...>(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<typename... Type>
|
||||
[[nodiscard]] decltype(auto) any_of() const {
|
||||
return owner_or_assert().template any_of<Type...>(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<typename... Type>
|
||||
[[nodiscard]] decltype(auto) get() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
return owner_or_assert().template get<Type...>(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<typename Type, typename... Args>
|
||||
[[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const {
|
||||
static_assert(((sizeof...(Scope) == 0) || ... || stl::is_same_v<Type, Scope>), "Invalid type");
|
||||
return owner_or_assert().template get_or_emplace<Type>(entt, stl::forward<Args>(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<typename... Type>
|
||||
[[nodiscard]] auto try_get() const {
|
||||
static_assert(sizeof...(Scope) == 0 || (type_list_contains_v<type_list<Scope...>, Type> && ...), "Invalid type");
|
||||
return owner_or_assert().template try_get<Type...>(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<typename... Other>
|
||||
[[nodiscard]] bool operator==(const basic_handle<Other...> &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<typename Other, typename... Args>
|
||||
operator basic_handle<Other, Args...>() const noexcept {
|
||||
static_assert(stl::is_same_v<Other, Registry> || stl::is_same_v<stl::remove_const_t<Other>, Registry>, "Invalid conversion between different handles");
|
||||
static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v<type_list<Scope...>, Args>))), "Invalid conversion between different handles");
|
||||
return owner ? basic_handle<Other, Args...>{*owner, entt} : basic_handle<Other, Args...>{};
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *owner;
|
||||
entity_type entt;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
256
include/entt/entity/helper.hpp
Normal file
256
include/entt/entity/helper.hpp
Normal file
@@ -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<typename Registry>
|
||||
class as_view {
|
||||
template<typename... Get, typename... Exclude>
|
||||
[[nodiscard]] auto dispatch(get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
return reg->template view<constness_as_t<typename Get::element_type, Get>...>(exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
|
||||
}
|
||||
|
||||
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<typename Get, typename Exclude>
|
||||
operator basic_view<Get, Exclude>() const {
|
||||
return dispatch(Get{}, Exclude{});
|
||||
}
|
||||
|
||||
private:
|
||||
registry_type *reg;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Converts a registry to a group.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
class as_group {
|
||||
template<typename... Owned, typename... Get, typename... Exclude>
|
||||
[[nodiscard]] auto dispatch(owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>) const {
|
||||
if constexpr(stl::is_const_v<registry_type>) {
|
||||
return reg->template group_if_exists<typename Owned::element_type...>(get_t<typename Get::element_type...>{}, exclude_t<typename Exclude::element_type...>{});
|
||||
} else {
|
||||
return reg->template group<constness_as_t<typename Owned::element_type, Owned>...>(get_t<constness_as_t<typename Get::element_type, Get>...>{}, exclude_t<constness_as_t<typename Exclude::element_type, Exclude>...>{});
|
||||
}
|
||||
}
|
||||
|
||||
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<typename Owned, typename Get, typename Exclude>
|
||||
operator basic_group<Owned, Get, Exclude>() 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<auto Member, typename Registry = stl::decay_t<nth_argument_t<0u, decltype(Member)>>>
|
||||
void invoke(Registry ®, const typename Registry::entity_type entt) {
|
||||
static_assert(stl::is_member_function_pointer_v<decltype(Member)>, "Invalid pointer to non-static member function");
|
||||
(reg.template get<member_class_t<decltype(Member)>>(entt).*Member)(reg, entt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the entity associated with a given element.
|
||||
*
|
||||
* @warning
|
||||
* Currently, this function only works correctly with the default storage as it
|
||||
* makes assumptions about how the elements are laid out.
|
||||
*
|
||||
* @tparam Args Storage type template parameters.
|
||||
* @param storage A storage that contains the given element.
|
||||
* @param instance A valid element instance.
|
||||
* @return The entity associated with the given element.
|
||||
*/
|
||||
template<typename... Args>
|
||||
basic_storage<Args...>::entity_type to_entity(const basic_storage<Args...> &storage, const typename basic_storage<Args...>::value_type &instance) {
|
||||
using traits_type = component_traits<typename basic_storage<Args...>::value_type, typename basic_storage<Args...>::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<decltype(dist)>(traits_type::page_size)) {
|
||||
return *(static_cast<const basic_storage<Args...>::base_type &>(storage).rbegin() + static_cast<decltype(dist)>(pos) + dist);
|
||||
}
|
||||
}
|
||||
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/*! @brief Primary template isn't defined on purpose. */
|
||||
template<typename...>
|
||||
struct sigh_helper;
|
||||
|
||||
/**
|
||||
* @brief Signal connection helper for registries.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
struct sigh_helper<Registry> {
|
||||
/*! @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<typename Type>
|
||||
auto with(const id_type id = type_hash<Type>::value()) noexcept {
|
||||
return sigh_helper<registry_type, Type>{*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<typename Registry, typename Type>
|
||||
struct sigh_helper<Registry, Type> final: sigh_helper<Registry> {
|
||||
/*! @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<Type>::value())
|
||||
: sigh_helper<Registry>{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 Candidate, typename... Args>
|
||||
auto on_construct(Args &&...args) {
|
||||
this->registry().template on_construct<Type>(name).template connect<Candidate>(stl::forward<Args>(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 Candidate, typename... Args>
|
||||
auto on_update(Args &&...args) {
|
||||
this->registry().template on_update<Type>(name).template connect<Candidate>(stl::forward<Args>(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 Candidate, typename... Args>
|
||||
auto on_destroy(Args &&...args) {
|
||||
this->registry().template on_destroy<Type>(name).template connect<Candidate>(stl::forward<Args>(args)...);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
id_type name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Deduction guide.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
sigh_helper(Registry &) -> sigh_helper<Registry>;
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
593
include/entt/entity/mixin.hpp
Normal file
593
include/entt/entity/mixin.hpp
Normal file
@@ -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<typename, typename>
|
||||
struct has_on_construct final: stl::false_type {};
|
||||
|
||||
template<typename Type, typename Registry>
|
||||
requires stl::invocable<decltype(&Type::on_construct), Registry &, typename Registry::entity_type>
|
||||
struct has_on_construct<Type, Registry>: stl::true_type {};
|
||||
|
||||
template<typename, typename>
|
||||
struct has_on_update final: stl::false_type {};
|
||||
|
||||
template<typename Type, typename Registry>
|
||||
requires stl::invocable<decltype(&Type::on_update), Registry &, typename Registry::entity_type>
|
||||
struct has_on_update<Type, Registry>: stl::true_type {};
|
||||
|
||||
template<typename, typename>
|
||||
struct has_on_destroy final: stl::false_type {};
|
||||
|
||||
template<typename Type, typename Registry>
|
||||
requires stl::invocable<decltype(&Type::on_destroy), Registry &, typename Registry::entity_type>
|
||||
struct has_on_destroy<Type, Registry>: 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> &, entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* This applies to all signals made available.
|
||||
*
|
||||
* @tparam Type Underlying storage type.
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Type, typename Registry>
|
||||
class basic_sigh_mixin final: public Type {
|
||||
using underlying_type = Type;
|
||||
using owner_type = Registry;
|
||||
|
||||
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
|
||||
using sigh_type = sigh<void(owner_type &, const typename underlying_type::entity_type), typename underlying_type::allocator_type>;
|
||||
using underlying_iterator = underlying_type::base_type::basic_iterator;
|
||||
|
||||
static_assert(stl::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
|
||||
|
||||
[[nodiscard]] auto &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<owner_type &>(*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<typename underlying_type::element_type, entity_type>) {
|
||||
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<underlying_type::base_type &>(*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<basic_registry_type>(&value);
|
||||
|
||||
if constexpr(!stl::is_same_v<registry_type, basic_registry_type>) {
|
||||
if(owner == nullptr) {
|
||||
owner = any_cast<registry_type>(&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<typename underlying_type::element_type, Registry>::value) {
|
||||
sink{construction}.template connect<&underlying_type::element_type::on_construct>();
|
||||
}
|
||||
|
||||
if constexpr(internal::has_on_update<typename underlying_type::element_type, Registry>::value) {
|
||||
sink{update}.template connect<&underlying_type::element_type::on_update>();
|
||||
}
|
||||
|
||||
if constexpr(internal::has_on_destroy<typename underlying_type::element_type, Registry>::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<underlying_type &&>(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<underlying_type &&>(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.<br/>
|
||||
* 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.<br/>
|
||||
* 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.<br/>
|
||||
* 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<stl::output_iterator<entity_type> 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<typename... Args>
|
||||
decltype(auto) emplace(const entity_type entt, Args &&...args) {
|
||||
underlying_type::emplace(entt, stl::forward<Args>(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<typename... Func>
|
||||
decltype(auto) patch(const entity_type entt, Func &&...func) {
|
||||
underlying_type::patch(entt, stl::forward<Func>(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<typename... Args>
|
||||
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>(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<typename Type, typename Registry>
|
||||
class basic_reactive_mixin final: public Type {
|
||||
using underlying_type = Type;
|
||||
using owner_type = Registry;
|
||||
|
||||
using alloc_traits = stl::allocator_traits<typename underlying_type::allocator_type>;
|
||||
using basic_registry_type = basic_registry<typename owner_type::entity_type, typename owner_type::allocator_type>;
|
||||
using container_type = stl::vector<connection, typename alloc_traits::template rebind_alloc<connection>>;
|
||||
|
||||
static_assert(stl::is_base_of_v<basic_registry_type, owner_type>, "Invalid registry type");
|
||||
|
||||
[[nodiscard]] auto &owner_or_assert() const noexcept {
|
||||
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
|
||||
return static_cast<owner_type &>(*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<basic_registry_type>(&value);
|
||||
|
||||
if constexpr(!stl::is_same_v<registry_type, basic_registry_type>) {
|
||||
if(owner == nullptr) {
|
||||
owner = any_cast<registry_type>(&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<underlying_type &&>(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<underlying_type &&>(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<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_construct(const id_type id = type_hash<Clazz>::value()) {
|
||||
auto curr = owner_or_assert().template storage<Clazz>(id).on_construct().template connect<Candidate>(*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<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_update(const id_type id = type_hash<Clazz>::value()) {
|
||||
auto curr = owner_or_assert().template storage<Clazz>(id).on_update().template connect<Candidate>(*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<typename Clazz, auto Candidate = &basic_reactive_mixin::emplace_element>
|
||||
basic_reactive_mixin &on_destroy(const id_type id = type_hash<Clazz>::value()) {
|
||||
auto curr = owner_or_assert().template storage<Clazz>(id).on_destroy().template connect<Candidate>(*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<typename... Get, typename... Exclude>
|
||||
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>>
|
||||
view(exclude_t<Exclude...> = exclude_t{}) const {
|
||||
const owner_type &parent = owner_or_assert();
|
||||
basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<const Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<const Exclude>...>> elem{};
|
||||
[&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }(parent.template storage<stl::remove_const_t<Exclude>>()..., parent.template storage<stl::remove_const_t<Get>>()..., this);
|
||||
return elem;
|
||||
}
|
||||
|
||||
/*! @copydoc view */
|
||||
template<typename... Get, typename... Exclude>
|
||||
[[nodiscard]] basic_view<get_t<const basic_reactive_mixin, typename basic_registry_type::template storage_for_type<Get>...>, exclude_t<typename basic_registry_type::template storage_for_type<Exclude>...>>
|
||||
view(exclude_t<Exclude...> = exclude_t{}) {
|
||||
stl::conditional_t<((stl::is_const_v<Get> && ...) && (stl::is_const_v<Exclude> && ...)), const owner_type, owner_type> &parent = owner_or_assert();
|
||||
return {*this, parent.template storage<stl::remove_const_t<Get>>()..., parent.template storage<stl::remove_const_t<Exclude>>()...};
|
||||
}
|
||||
|
||||
/*! @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
|
||||
437
include/entt/entity/organizer.hpp
Normal file
437
include/entt/entity/organizer.hpp
Normal file
@@ -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<typename>
|
||||
struct is_view: stl::false_type {};
|
||||
|
||||
template<typename... Args>
|
||||
struct is_view<basic_view<Args...>>: stl::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool is_view_v = is_view<Type>::value;
|
||||
|
||||
template<typename>
|
||||
struct is_group: stl::false_type {};
|
||||
|
||||
template<typename... Args>
|
||||
struct is_group<basic_group<Args...>>: stl::true_type {};
|
||||
|
||||
template<typename Type>
|
||||
inline constexpr bool is_group_v = is_group<Type>::value;
|
||||
|
||||
template<typename Type, typename Override>
|
||||
struct unpack_type {
|
||||
using ro = stl::conditional_t<
|
||||
type_list_contains_v<Override, const Type> || (stl::is_const_v<Type> && !type_list_contains_v<Override, stl::remove_const_t<Type>>),
|
||||
type_list<stl::remove_const_t<Type>>,
|
||||
type_list<>>;
|
||||
|
||||
using rw = stl::conditional_t<
|
||||
type_list_contains_v<Override, stl::remove_const_t<Type>> || (!stl::is_const_v<Type> && !type_list_contains_v<Override, const Type>),
|
||||
type_list<Type>,
|
||||
type_list<>>;
|
||||
};
|
||||
|
||||
template<typename... Args, typename... Override>
|
||||
struct unpack_type<basic_registry<Args...>, type_list<Override...>> {
|
||||
using ro = type_list<>;
|
||||
using rw = type_list<>;
|
||||
};
|
||||
|
||||
template<typename... Args, typename... Override>
|
||||
struct unpack_type<const basic_registry<Args...>, type_list<Override...>>
|
||||
: unpack_type<basic_registry<Args...>, type_list<Override...>> {};
|
||||
|
||||
template<typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
|
||||
using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw...>;
|
||||
};
|
||||
|
||||
template<typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<const basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
|
||||
: unpack_type<basic_view<get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
|
||||
|
||||
template<typename... Owned, typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {
|
||||
using ro = type_list_cat_t<type_list<typename Exclude::element_type...>, typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::ro..., typename unpack_type<constness_as_t<typename Owned::element_type, Owned>, type_list<Override...>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<constness_as_t<typename Get::element_type, Get>, type_list<Override...>>::rw..., typename unpack_type<constness_as_t<typename Owned::element_type, Owned>, type_list<Override...>>::rw...>;
|
||||
};
|
||||
|
||||
template<typename... Owned, typename... Get, typename... Exclude, typename... Override>
|
||||
struct unpack_type<const basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>>
|
||||
: unpack_type<basic_group<owned_t<Owned...>, get_t<Get...>, exclude_t<Exclude...>>, type_list<Override...>> {};
|
||||
|
||||
template<typename, typename, typename>
|
||||
struct resource_traits;
|
||||
|
||||
template<typename Registry, typename... Args, typename... Req>
|
||||
struct resource_traits<Registry, type_list<Args...>, type_list<Req...>> {
|
||||
using args = type_list<stl::remove_const_t<Args>...>;
|
||||
using ro = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::ro..., typename unpack_type<Req, type_list<>>::ro...>;
|
||||
using rw = type_list_cat_t<typename unpack_type<Args, type_list<Req...>>::rw..., typename unpack_type<Req, type_list<>>::rw...>;
|
||||
static constexpr auto sync_point = (stl::is_same_v<Args, Registry> || ...);
|
||||
};
|
||||
|
||||
template<typename Registry, typename... Req, typename Ret, typename... Args>
|
||||
resource_traits<Registry, type_list<stl::remove_reference_t<Args>...>, type_list<Req...>> free_function_to_resource_traits(Ret (*)(Args...));
|
||||
|
||||
template<typename Registry, typename... Req, typename Ret, typename Type, typename... Args>
|
||||
resource_traits<Registry, type_list<stl::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (*)(Type &, Args...));
|
||||
|
||||
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<Registry, type_list<stl::remove_reference_t<Args>...>, type_list<Req...>> constrained_function_to_resource_traits(Ret (Class::*)(Args...));
|
||||
|
||||
template<typename Registry, typename... Req, typename Ret, typename Class, typename... Args>
|
||||
resource_traits<Registry, type_list<stl::remove_reference_t<Args>...>, type_list<Req...>> 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.<br/>
|
||||
* Note that the resulting tasks aren't executed in any case. This isn't the
|
||||
* goal of the tool. Instead, they are returned to the user in the form of a
|
||||
* graph that allows for safe execution.
|
||||
*
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
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<typename Type>
|
||||
[[nodiscard]] static decltype(auto) extract(Registry ®) {
|
||||
if constexpr(stl::is_same_v<Type, Registry>) {
|
||||
return reg;
|
||||
} else if constexpr(internal::is_view_v<Type>) {
|
||||
return static_cast<Type>(as_view{reg});
|
||||
} else if constexpr(internal::is_group_v<Type>) {
|
||||
return static_cast<Type>(as_group{reg});
|
||||
} else {
|
||||
return reg.ctx().template emplace<stl::remove_reference_t<Type>>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
[[nodiscard]] static auto to_args(Registry ®, type_list<Args...>) {
|
||||
return stl::tuple<decltype(extract<Args>(reg))...>(extract<Args>(reg)...);
|
||||
}
|
||||
|
||||
template<typename... Type>
|
||||
[[nodiscard]] static stl::size_t fill_dependencies(type_list<Type...>, [[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<Type>()...};
|
||||
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<typename... RO, typename... RW>
|
||||
void track_dependencies(stl::size_t index, const bool sync_point, type_list<RO...>, type_list<RW...>) {
|
||||
builder.bind(static_cast<id_type>(index));
|
||||
builder.set(type_hash<Registry>::value(), sync_point || (sizeof...(RO) + sizeof...(RW) == 0u));
|
||||
(builder.ro(type_hash<RO>::value()), ...);
|
||||
(builder.rw(type_hash<RW>::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<stl::size_t> from, stl::vector<stl::size_t> 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<stl::size_t> &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<stl::size_t> &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<stl::size_t> in;
|
||||
stl::vector<stl::size_t> 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<auto Candidate, typename... Req>
|
||||
void emplace(const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::free_function_to_resource_traits<registry_type, Req...>(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<stl::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
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<auto Candidate, typename... Req, typename Type>
|
||||
void emplace(Type &value_or_instance, const char *name = nullptr) {
|
||||
using resource_type = decltype(internal::constrained_function_to_resource_traits<registry_type, Req...>(Candidate));
|
||||
|
||||
callback_type *callback = +[](const void *payload, registry_type ®) {
|
||||
Type *curr = static_cast<Type *>(const_cast<constness_as_t<void, Type> *>(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<stl::integral_constant<decltype(Candidate), Candidate>>()};
|
||||
|
||||
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<typename... Req>
|
||||
void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) {
|
||||
using resource_type = internal::resource_traits<registry_type, type_list<>, type_list<Req...>>;
|
||||
track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{});
|
||||
|
||||
vertex_data vdata{
|
||||
resource_type::ro::size,
|
||||
resource_type::rw::size,
|
||||
name,
|
||||
payload,
|
||||
func,
|
||||
+[](const bool rw, const type_info **buffer, const 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<void>()};
|
||||
|
||||
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<vertex> graph() const {
|
||||
stl::vector<vertex> adjacency_list{};
|
||||
adjacency_list.reserve(vertices.size());
|
||||
|
||||
for(auto adjacency_matrix = builder.graph(); auto curr: adjacency_matrix.vertices()) {
|
||||
stl::vector<stl::size_t> in{};
|
||||
stl::vector<stl::size_t> 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<vertex_data> vertices;
|
||||
flow builder;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
28
include/entt/entity/ranges.hpp
Normal file
28
include/entt/entity/ranges.hpp
Normal file
@@ -0,0 +1,28 @@
|
||||
#ifndef ENTT_ENTITY_RANGES_HPP
|
||||
#define ENTT_ENTITY_RANGES_HPP
|
||||
|
||||
#include <version>
|
||||
|
||||
#if defined(__cpp_lib_ranges)
|
||||
# include <ranges>
|
||||
# include "fwd.hpp"
|
||||
|
||||
namespace std::ranges {
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool enable_borrowed_range<entt::basic_view<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool enable_borrowed_range<entt::basic_group<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool enable_view<entt::basic_view<Args...>>{true};
|
||||
|
||||
template<class... Args>
|
||||
inline constexpr bool enable_view<entt::basic_group<Args...>>{true};
|
||||
|
||||
} // namespace std::ranges
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1181
include/entt/entity/registry.hpp
Normal file
1181
include/entt/entity/registry.hpp
Normal file
File diff suppressed because it is too large
Load Diff
322
include/entt/entity/runtime_view.hpp
Normal file
322
include/entt/entity/runtime_view.hpp
Normal file
@@ -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<typename Set>
|
||||
class runtime_view_iterator final {
|
||||
using iterator_type = Set::iterator;
|
||||
using iterator_traits = stl::iterator_traits<iterator_type>;
|
||||
|
||||
[[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<Set *> &cpools, iterator_type curr, const stl::vector<Set *> &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<Set *> *pools;
|
||||
const stl::vector<Set *> *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<typename Type, typename Allocator>
|
||||
class basic_runtime_view {
|
||||
using alloc_traits = stl::allocator_traits<Allocator>;
|
||||
static_assert(stl::is_same_v<typename alloc_traits::value_type, Type *>, "Invalid value type");
|
||||
using container_type = stl::vector<Type *, Allocator>;
|
||||
|
||||
[[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<common_type>;
|
||||
|
||||
/*! @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<difference_type>(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.<br/>
|
||||
* The signature of the function should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* void(const entity_type);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Func Type of the function object to invoke.
|
||||
* @param func A valid function object.
|
||||
*/
|
||||
template<typename Func>
|
||||
void each(Func func) const {
|
||||
for(const auto entity: *this) {
|
||||
func(entity);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
container_type pools;
|
||||
container_type filter;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
509
include/entt/entity/snapshot.hpp
Normal file
509
include/entt/entity/snapshot.hpp
Normal file
@@ -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<typename Registry>
|
||||
void orphans(Registry ®istry) {
|
||||
for(auto &storage = registry.template storage<typename Registry::entity_type>(); 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.<br/>
|
||||
* This type can be used in both cases if provided with a correctly configured
|
||||
* output archive.
|
||||
*
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
class basic_snapshot {
|
||||
static_assert(!stl::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
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<typename Type, typename Archive>
|
||||
const basic_snapshot &get(Archive &archive, const id_type id = type_hash<Type>::value()) const {
|
||||
if(const auto *storage = reg->template storage<Type>(id); storage) {
|
||||
const typename registry_type::common_type &base = *storage;
|
||||
|
||||
archive(static_cast<traits_type::entity_type>(storage->size()));
|
||||
|
||||
if constexpr(stl::is_same_v<Type, entity_type>) {
|
||||
archive(static_cast<traits_type::entity_type>(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<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<entity_type>(null));
|
||||
} else {
|
||||
archive(entt);
|
||||
stl::apply([&archive](auto &&...args) { (archive(stl::forward<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for(auto elem: storage->reach()) {
|
||||
stl::apply([&archive](auto &&...args) { (archive(stl::forward<decltype(args)>(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<typename Type, typename Archive>
|
||||
const basic_snapshot &get(Archive &archive, stl::input_iterator auto first, stl::input_iterator auto last, const id_type id = type_hash<Type>::value()) const {
|
||||
static_assert(!stl::is_same_v<Type, entity_type>, "Entity types not supported");
|
||||
|
||||
if(const auto *storage = reg->template storage<Type>(id); storage && !storage->empty()) {
|
||||
archive(static_cast<traits_type::entity_type>(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<decltype(args)>(args)), ...); }, storage->get_as_tuple(entt));
|
||||
} else {
|
||||
archive(static_cast<entity_type>(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.<br/>
|
||||
* An example of use is the implementation of a save/restore utility.
|
||||
*
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
class basic_snapshot_loader {
|
||||
static_assert(!stl::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
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<entity_type>().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<typename Type, typename Archive>
|
||||
basic_snapshot_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
|
||||
auto &storage = reg->template storage<Type>(id);
|
||||
typename traits_type::entity_type length{};
|
||||
|
||||
archive(length);
|
||||
|
||||
if constexpr(stl::is_same_v<Type, entity_type>) {
|
||||
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>();
|
||||
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<decltype(storage.get_as_tuple({}))> == 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.<br/>
|
||||
* 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.<br/>
|
||||
* Identifiers that entities originally had are not transferred to the target.
|
||||
* Instead, the loader maps remote identifiers to local ones while restoring a
|
||||
* snapshot.<br/>
|
||||
* An example of use is the implementation of a client-server application with
|
||||
* the requirement of transferring somehow parts of the representation side to
|
||||
* side.
|
||||
*
|
||||
* @tparam Registry Basic registry type.
|
||||
*/
|
||||
template<typename Registry>
|
||||
class basic_continuous_loader {
|
||||
static_assert(!stl::is_const_v<Registry>, "Non-const registry type required");
|
||||
using traits_type = entt_traits<typename Registry::entity_type>;
|
||||
|
||||
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<typename Container>
|
||||
auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) {
|
||||
// map like container
|
||||
Container other;
|
||||
|
||||
for(auto &&pair: container) {
|
||||
using first_type = stl::remove_const_t<typename stl::decay_t<decltype(pair)>::first_type>;
|
||||
using second_type = stl::decay_t<decltype(pair)>::second_type;
|
||||
|
||||
if constexpr(stl::is_same_v<first_type, entity_type> && stl::is_same_v<second_type, entity_type>) {
|
||||
other.emplace(map(pair.first), map(pair.second));
|
||||
} else if constexpr(stl::is_same_v<first_type, entity_type>) {
|
||||
other.emplace(map(pair.first), stl::move(pair.second));
|
||||
} else {
|
||||
static_assert(stl::is_same_v<second_type, entity_type>, "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<typename Container>
|
||||
auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) {
|
||||
// vector like container
|
||||
static_assert(stl::is_same_v<typename Container::value_type, entity_type>, "Invalid value type");
|
||||
|
||||
for(auto &&entt: container) {
|
||||
entt = map(entt);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Component, typename Other, typename Member>
|
||||
void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*member) {
|
||||
if constexpr(!stl::is_same_v<Component, Other>) {
|
||||
return;
|
||||
} else if constexpr(stl::is_same_v<Member, entity_type>) {
|
||||
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.<br/>
|
||||
* 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<typename Type, typename Archive>
|
||||
basic_continuous_loader &get(Archive &archive, const id_type id = type_hash<Type>::value()) {
|
||||
auto &storage = reg->template storage<Type>(id);
|
||||
typename traits_type::entity_type length{};
|
||||
entity_type entt{null};
|
||||
|
||||
archive(length);
|
||||
|
||||
if constexpr(stl::is_same_v<Type, entity_type>) {
|
||||
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<decltype(storage.get_as_tuple({}))> == 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.<br/>
|
||||
* 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<typename traits_type::entity_type, stl::pair<entity_type, entity_type>> remloc;
|
||||
registry_type *reg;
|
||||
};
|
||||
|
||||
} // namespace entt
|
||||
|
||||
#endif
|
||||
1076
include/entt/entity/sparse_set.hpp
Normal file
1076
include/entt/entity/sparse_set.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1222
include/entt/entity/storage.hpp
Normal file
1222
include/entt/entity/storage.hpp
Normal file
File diff suppressed because it is too large
Load Diff
1142
include/entt/entity/view.hpp
Normal file
1142
include/entt/entity/view.hpp
Normal file
File diff suppressed because it is too large
Load Diff
93
include/entt/entt.hpp
Normal file
93
include/entt/entt.hpp
Normal file
@@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user