Files
Cubed/include/entt/meta/meta.hpp
zhenyan121 236e7c0433 feature: creature (#38)
* build(deps): add assimp library as dependency

* feat(render): add model loading and rendering pipeline

* refactor(render): rename depth player shaders to depth model and clean up

* build: add EnTT library

* feat(entity): add ECS-based entity rendering

* feat(render): add shadow pass for entity models

* refactor(render): unify model and player rendering with single shader pipeline

* refactor(collision): convert AABB to center-half representation and migrate player data to ECS

* refactor(gameplay): add base classes Chunk and World for shared logic

* feat(gameplay): implement entity movement system with collision detection

- Add Velocity and HitBoxes components to entity
- Introduce HitboxManager for loading per-entity collision AABBs from JSON
- Create MoveSystem with per-axis collision handling
- Move get_block_aabb to base World class and add virtual get_per_tick_time
- Remove static get_block_aabb from ClientWorld; use member m_per_tick_time for tick duration

* fix(gameplay): correct hitbox insertion in HitboxManager::load

* build: remove entt library

* refactor(gameplay): replace entt components with SparseVector for player data

Refactor client world to use a custom SparseVector for player data instead of entt registry. Consolidate Transform, ViewAngles, and related structs into Position, Orientation, WalkPose. Introduce PlayerData and PlayerRenderData. Remove the unused move_system.cpp. Unify player render and shadow render into a single function.

* feat(render): add model ID system with concurrent lookup and namespace-based loading

- Extract `ModelID` and `Model` struct to new `model.hpp`
- Replace `std::unordered_map` with `tbb::concurrent_hash_map` for thread safety
- Add `get_model(ModelID)`, `get_model_id`, `get_model_name` methods
- Parse model names in `namespace:name` format to construct asset paths
- Update `load_model` to accept `string_view` and use ID-based management

* refactor(gameplay): extract movement, gravity, orientation, and walk pose into structs

* refactor(gameplay): extract Entity base class from ClientPlayer

Move position, walk pose, velocity, orientation, movement, and gravity
fields and their accessors into a new Entity base class. ClientPlayer now
inherits from Entity, removing duplicated members. Also update velocity
handling to use 3D vector per axis and adjust related logic.

* feat(player): split max speed into horizontal/vertical, set spawn pos

* refactor: move movement logic into SpeedSystem and Entity components

* refactor(gameplay): extract physics and collision from ClientPlayer into PhysicalSystem

Move per-axis collision detection and move distance calculation to new
PhysicalSystem. Move SpeedSystem implementation from inline header to
separate .cpp file. Add const accessors to Entity. Replace inline AABB
helper with HitboxManager registration of player hitbox using new
PLAYER_SIZE constant. Remove obsolete members and functions from
ClientPlayer.

* refactor(gameplay): integrate model and hitbox ID system into Entity

- Introduce HitboxID, EntityID, and ModelID types for safer ID-based lookups
- Replace AABB struct with Hitbox (includes HitboxID)
- Convert ModelManager and HitboxManager to singletons with Handle structs
- Update Entity to store IDs for model and hitbox, removing direct references
- Reorganize creature model assets into subdirectories per entity type
- Add player model (player.glb) and collision data for pig
- Remove ModelManager dependency from App and Renderer
- Add namespace parsing utility for asset paths
- Mark sparse_vector::insert() with [[nodiscard]]

* build: add entt library

* refactor(gameplay): rename ClientPlayer to LocalPlayer

* refactor(gameplay): separate player logic into manager and ECS components

* feat(gameplay): add entity managers and refactor to ECS components

* refactor(gameplay): replace client thread with timer-based system and add entity manager

* feat(gameplay): implement entity system with concurrent task handling and fix model loading

* feat(gameplay): add entity destruction support

* feat(entity): add client-to-server entity creation and destruction requests

Extend the network protocol with `C2SEntityCreateRequest` and `C2SEntityDestoryRequest` packets. Refactor entity managers to split public client-facing create/destroy methods that send requests over the network, from internal handlers that process received packets. Add utility functions for converting protobuf Vec3 to glm::vec3.

* feat(gameplay): implement entity update packets

Add S2CEntityUpdate to sync entity positions from server to client, including update handling in the client entity manager and server-side AI updates.

* feat(ai): add wander AI system with move boost

Add AIBase and WanderAITag components, a WanderAISystem, and a MoveBoost component to control wandering behavior. Replace the old MoveState with MoveBoost and include a horizontal random direction helper.

* refactor(ecs): move entity physics to tick-based systems

Update PhysicalSystem and SpeedSystem to operate on entt::registry
instead of individual components. Add TickVelocity for server creatures
so movement is calculated per tick without frame delta time. LocalPlayer
now implements its own client-side physics with collision detection.

* feat(gameplay): add server-side entity movement and AI

Refactor LocalPlayer::update_physical to operate on a passed position, and run speed, physical, and wander AI systems in ServerEntityManager.

* feat(gameplay): add pig wander AI and refine speed physics

* feat(server): send entity position updates to all sessions

* fix: correct velocity clamping and hitbox loading

Preserve sign when clamping velocity; negative velocities now clamp to zero instead of flipping direction. Support loading hitbox definitions from JSON arrays and objects. Tune pig entity movement, spawn height, and wander probabilities.

* feat(creatures): refine pig movement physics

Add movement constants for pigs and switch deceleration to per-axis friction so creatures stop naturally. Extend wander boost duration for smoother behavior.

* feat(physics): implement step-up for horizontal collisions

* feat: sync entity direction and rotate models accordingly

Move direction into the transform component and include it in server-to-client entity updates. Client now sets transform direction from the network message and uses it to compute yaw for model rotation, so entities visually face their movement direction. Refactor net_utils to support arbitrary Vec3 fields.

* feat(render): interpolate entity transforms for rendering

Add RenderTransform component to smooth position/direction updates
and use it in entity and shadow passes.

* fix(server_world): send time before entity updates

* build: replace nlohmann json with rapidjson

* refactor(json): migrate from nlohmann to rapidjson

Replace nlohmann::json with rapidjson across localization, hitbox manager, server world, and sensitive filter. Add json_utils helper for converting rapidjson documents to maps, and improve parse error handling.

* feat(item): add item manager with JSON asset loading

Implement ItemManager to load and query item definitions from assets/item JSON files. Add ItemData struct, item asset metadata, CMake source registration, and initialize the manager during app startup. Also add AGENTS.md repository guidelines.

* refactor(item): decouple items from block types

The item system now supports an ItemKind and property, allowing item JSON to declare a type instead of assuming every item is a block. BlockManager was moved to its own header, redesigned around concurrent hash maps, and exposes id_from_name(). Texture and UI code now key items by ItemID, while block placement resolves the block type from the item registry.

* fix(gameplay): use inline const for static EMPTY members

Use inline const for static EMPTY members to avoid ODR and linker issues.
Add missing block_manager.hpp includes in gameplay sources.

* fix: stabilize block item registration and display

Store item names as owned strings in ItemManager, set block type
property for block items, validate item kind before placement, and
handle items without textures gracefully in inventory UI.

* feat: add pig spawn egg item

Add pig spawn egg asset and texture, parse spawn egg item kind and creature property, and spawn the configured entity when used on an empty block. Also fix item texture loading to use actual image dimensions and update selected item UI sizing.

* refactor(gameplay): update systems per entity with chunk check

Refactor server entity update flow to process entities individually,
skipping those whose chunk is not loaded. System update methods now
accept a single entity instead of iterating the full registry view,
and `ServerWorld::get_chunk_ref_count` is added to determine if an
entity's chunk is active.

* perf(render): skip entities outside loaded chunks

* feat: add --direct-enter option to skip to world scene

Support a new CLI flag that launches the app directly into the world scene. If no IP is provided, it starts a local server on the specified port and connects to 127.0.0.1; otherwise it connects to the given IP. With this flag enabled, a port must be specified via -p. Also expose SceneManager::push as public so scenes can be pushed immediately when bypassing the normal menu flow.

* refactor(localization): switch block translations to item naming

Remove the `name_key` field from block definitions and use item-based
localization keys (`item.*.name`) for inventory display. Item data now
stores the localized name at load time.

* feat(gameplay): spawn creatures during chunk generation

Add creature spawning to chunk generation with a configurable
SpawnConfig, including a default pig spawn. Spawning occurs in
the final generation phase and registers entities through the
server world's entity manager.

Also destroy entities when their chunk is unloaded to prevent
orphaned entities and expose the entity manager from ServerWorld.

* feat(gameplay): add run mode based thread pool config

Introduce RunMode enum and thread pool sizing helpers for client,
server, and hybrid modes. Add compute pool to server world and pass
mode through server/client initialization.

* perf(server): parallelize entity update loop

Use parallel_do to process entities concurrently via the compute
pool. Switch get_all_session to a thread-safe
tbb::concurrent_vector and make ChunkEntity's ref_count atomic to
avoid data races. Also remove the debug pig spawn from world init.

* feat(creatures): animate creature walk cycles

Add a Gait component synchronized over the network and procedural animation for model nodes. Load animation parameters from assets/model/creature/pig/animation.json and apply leg swing, body bob, and head motion based on gait. Refactor Gait into its own header for reuse.

* fix(gameplay): make entity interpolation frame-rate independent

* perf(render): batch model rendering with instancing

Replaces per-entity draw calls with instanced rendering for both main and shadow passes. Each model's node hierarchy is flattened once and instance matrices are updated per frame, reducing draw calls and CPU overhead.

* refactor(render): build entity instance buffers once per frame

Move instance matrix upload into build_vertices and use a precomputed
instance data map for both shadow and color passes, avoiding duplicate
GPU buffer updates.

* perf(gameplay): batch entity updates into single packet

Aggregate per-entity update messages into S2CEntityUpdateBatch and
broadcast once per tick. Reuse serialized packets in several broadcast
loops to avoid repeated make_packet calls.

* fix: correct thread count and shadow projection uniform

- Add reserved client thread count to client threads in server generation calculation.
- Set projection matrix uniform only for non-shadow rendering.

* feat(ecs): add entity type separation and creature limits

Replace generic entity creation with typed creatures and items. Rename `add_entity` to `add_creature`, enforce per-player creature cap, and track entity/creature totals for metrics. Expose run mode to dev panel and show new counts.

* perf(render): frustum cull entities before instance building

Add frustum culling to entity instance data collection using AABB vs camera frustum planes. Pass renderer into get_instances_data_map to compute MVP and extract planes. Also add "Rendered Entities" debug counter for visibility.

* feat(audio): add ambient pig sounds

Add pig call audio with randomized timing, triggered by proximity to the player as 3D positional sound.

* feat(client-entity): use snapshot history for entity interpolation

Replace exponential smoothing with a buffered snapshot system. Each entity keeps a deque of position/direction snapshots with timestamps; the render transform is interpolated at a fixed 100 ms render delay to hide network jitter. Snapshots are capped to 16 entries.

* refactor(hitbox): move player hitbox definition to JSON asset

* feat(client_player): use snapshot interpolation for remote players

Replace exponential position smoothing with snapshot interpolation. Remote player transforms are rendered from a delayed snapshot history, using linear interpolation for position and shortest-path interpolation for yaw/pitch. A 100 ms render delay compensates for network tick rate.
2026-08-07 10:19:49 +08:00

1903 lines
74 KiB
C++

#ifndef ENTT_META_META_HPP
#define ENTT_META_META_HPP
#include "../config/config.h"
#include "../core/any.hpp"
#include "../core/concepts.hpp"
#include "../core/fwd.hpp"
#include "../core/iterator.hpp"
#include "../core/type_info.hpp"
#include "../core/type_traits.hpp"
#include "../core/utility.hpp"
#include "../locator/locator.hpp"
#include "../stl/array.hpp"
#include "../stl/concepts.hpp"
#include "../stl/cstddef.hpp"
#include "../stl/iterator.hpp"
#include "../stl/memory.hpp"
#include "../stl/string_view.hpp"
#include "../stl/type_traits.hpp"
#include "../stl/utility.hpp"
#include "adl_pointer.hpp"
#include "context.hpp"
#include "fwd.hpp"
#include "node.hpp"
#include "range.hpp"
#include "type_traits.hpp"
namespace entt {
/*! @cond ENTT_INTERNAL */
namespace internal {
template<typename Type>
struct basic_meta_object {
[[nodiscard]] auto &node_or_assert() const noexcept {
ENTT_ASSERT(node != nullptr, "Invalid pointer to node");
return *node;
}
const Type *node{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
};
} // namespace internal
/*! @endcond */
/*! @brief Proxy object for sequence containers. */
class meta_sequence_container {
class meta_iterator;
public:
/*! @brief Unsigned integer type. */
using size_type = stl::size_t;
/*! @brief Meta iterator type. */
using iterator = meta_iterator;
/*! @brief Default constructor. */
meta_sequence_container() = default;
/**
* @brief Context aware constructor.
* @tparam Type Type of container to wrap.
* @param area The context from which to search for meta types.
* @param instance The container to wrap.
*/
template<typename Type>
meta_sequence_container(const meta_ctx &area, Type &instance) noexcept
: ctx{&area},
data{&instance},
value_type_node{&internal::resolve<typename Type::value_type>},
const_reference_node{&internal::resolve<stl::remove_cvref_t<typename Type::const_reference>>},
size_fn{meta_sequence_container_traits<stl::remove_const_t<Type>>::size},
clear_fn{meta_sequence_container_traits<stl::remove_const_t<Type>>::clear},
reserve_fn{meta_sequence_container_traits<stl::remove_const_t<Type>>::reserve},
resize_fn{meta_sequence_container_traits<stl::remove_const_t<Type>>::resize},
begin_end_fn{meta_sequence_container_traits<stl::remove_const_t<Type>>::iter},
insert_fn{meta_sequence_container_traits<stl::remove_const_t<Type>>::insert},
erase_fn{meta_sequence_container_traits<stl::remove_const_t<Type>>::erase},
const_only{stl::is_const_v<Type>} {}
[[nodiscard]] inline meta_type value_type() const noexcept;
[[nodiscard]] inline size_type size() const noexcept;
inline bool resize(size_type);
inline bool clear();
inline bool reserve(size_type);
[[nodiscard]] inline iterator begin();
[[nodiscard]] inline iterator end();
inline iterator insert(const iterator &, meta_any);
inline iterator erase(const iterator &);
[[nodiscard]] inline meta_any operator[](size_type);
[[nodiscard]] inline explicit operator bool() const noexcept;
private:
const meta_ctx *ctx{};
const void *data{};
const internal::meta_type_node &(*value_type_node)(const internal::meta_context &){};
const internal::meta_type_node &(*const_reference_node)(const internal::meta_context &){};
size_type (*size_fn)(const void *){};
bool (*clear_fn)(void *){};
bool (*reserve_fn)(void *, const size_type){};
bool (*resize_fn)(void *, const size_type){};
iterator (*begin_end_fn)(const meta_ctx &, void *, const void *, const bool){};
iterator (*insert_fn)(const meta_ctx &, void *, const void *, const void *, const iterator &){};
iterator (*erase_fn)(const meta_ctx &, void *, const iterator &){};
bool const_only{};
};
/*! @brief Proxy object for associative containers. */
class meta_associative_container {
class meta_iterator;
public:
/*! @brief Unsigned integer type. */
using size_type = stl::size_t;
/*! @brief Meta iterator type. */
using iterator = meta_iterator;
/*! @brief Default constructor. */
meta_associative_container() = default;
/**
* @brief Context aware constructor.
* @tparam Type Type of container to wrap.
* @param area The context from which to search for meta types.
* @param instance The container to wrap.
*/
template<typename Type>
meta_associative_container(const meta_ctx &area, Type &instance) noexcept
: ctx{&area},
data{&instance},
key_type_node{&internal::resolve<typename Type::key_type>},
value_type_node{&internal::resolve<typename Type::value_type>},
size_fn{&meta_associative_container_traits<stl::remove_const_t<Type>>::size},
clear_fn{&meta_associative_container_traits<stl::remove_const_t<Type>>::clear},
reserve_fn{&meta_associative_container_traits<stl::remove_const_t<Type>>::reserve},
begin_end_fn{&meta_associative_container_traits<stl::remove_const_t<Type>>::iter},
insert_fn{&meta_associative_container_traits<stl::remove_const_t<Type>>::insert},
erase_fn{&meta_associative_container_traits<stl::remove_const_t<Type>>::erase},
find_fn{&meta_associative_container_traits<stl::remove_const_t<Type>>::find},
const_only{stl::is_const_v<Type>} {
if constexpr(!meta_associative_container_traits<stl::remove_const_t<Type>>::key_only) {
mapped_type_node = &internal::resolve<typename Type::mapped_type>;
}
}
[[nodiscard]] inline meta_type key_type() const noexcept;
[[nodiscard]] inline meta_type mapped_type() const noexcept;
[[nodiscard]] inline meta_type value_type() const noexcept;
[[nodiscard]] inline size_type size() const noexcept;
inline bool clear();
inline bool reserve(size_type);
[[nodiscard]] inline iterator begin();
[[nodiscard]] inline iterator end();
inline bool insert(meta_any, meta_any);
inline size_type erase(meta_any);
[[nodiscard]] inline iterator find(meta_any);
[[nodiscard]] inline explicit operator bool() const noexcept;
private:
const meta_ctx *ctx{};
const void *data{};
const internal::meta_type_node &(*key_type_node)(const internal::meta_context &){};
const internal::meta_type_node &(*mapped_type_node)(const internal::meta_context &){};
const internal::meta_type_node &(*value_type_node)(const internal::meta_context &){};
size_type (*size_fn)(const void *){};
bool (*clear_fn)(void *){};
bool (*reserve_fn)(void *, const size_type){};
iterator (*begin_end_fn)(const meta_ctx &, void *, const void *, const bool){};
bool (*insert_fn)(void *, const void *, const void *){};
size_type (*erase_fn)(void *, const void *){};
iterator (*find_fn)(const meta_ctx &, void *, const void *, const void *){};
bool const_only{};
};
/*! @brief Opaque wrapper for values of any type. */
class meta_any {
using vtable_type = void(const internal::meta_traits, const meta_any &, void *);
template<cvref_unqualified Type>
static void basic_vtable(const internal::meta_traits req, const meta_any &value, [[maybe_unused]] void *other) {
if(req == internal::meta_traits::is_none) {
value.node = &internal::resolve<Type>(internal::meta_context::from(*value.ctx));
}
if constexpr(is_meta_pointer_like_v<Type>) {
if(req == internal::meta_traits::is_pointer) {
if constexpr(!stl::is_void_v<stl::remove_const_t<typename stl::pointer_traits<Type>::element_type>>) {
if constexpr(stl::is_constructible_v<bool, Type>) {
if(const auto &pointer_like = any_cast<const Type &>(value.storage); pointer_like) {
static_cast<meta_any *>(other)->emplace<decltype(adl_meta_pointer_like<Type>::dereference(stl::declval<const Type &>()))>(adl_meta_pointer_like<Type>::dereference(pointer_like));
}
} else {
static_cast<meta_any *>(other)->emplace<decltype(adl_meta_pointer_like<Type>::dereference(stl::declval<const Type &>()))>(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(value.storage)));
}
}
}
} else if constexpr(requires(Type elem) { *elem; }) {
if(req == internal::meta_traits::is_pointer) {
if constexpr(stl::is_class_v<Type>) {
if(const auto &elem = any_cast<const Type &>(value.storage); elem) {
return (value.storage.policy() == any_policy::cref) ? static_cast<meta_any *>(other)->emplace<decltype(*elem)>(*elem) : static_cast<meta_any *>(other)->emplace<decltype(*const_cast<Type &>(elem))>(*const_cast<Type &>(elem));
}
} else if constexpr(!stl::is_array_v<Type> && !stl::is_void_v<stl::remove_const_t<stl::remove_pointer_t<Type>>>) {
if(auto *pointer = any_cast<Type>(value.storage); pointer) {
static_cast<meta_any *>(other)->emplace<stl::conditional_t<stl::is_function_v<stl::remove_const_t<stl::remove_pointer_t<Type>>>, Type, stl::remove_pointer_t<Type> &>>(*pointer);
}
}
}
} else if constexpr(is_complete_v<meta_sequence_container_traits<Type>> || is_complete_v<meta_associative_container_traits<Type>>) {
if(constexpr auto flag = (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_sequence_container : internal::meta_traits::is_associative_container); req == flag) {
using container_type = stl::conditional_t<is_complete_v<meta_sequence_container_traits<Type>>, meta_sequence_container, meta_associative_container>;
*static_cast<container_type *>(other) = (value.storage.policy() == any_policy::cref) ? container_type{*value.ctx, any_cast<const Type &>(value.storage)} : container_type{*value.ctx, any_cast<Type &>(const_cast<meta_any &>(value).storage)};
}
}
}
[[nodiscard]] const auto &fetch_node() const {
if(node == nullptr) {
ENTT_ASSERT(*this, "Invalid vtable function");
vtable(internal::meta_traits::is_none, *this, nullptr);
}
ENTT_ASSERT(node != nullptr, "Invalid pointer to node");
return *node;
}
meta_any(const meta_any &other, any elem)
: storage{stl::move(elem)},
ctx{other.ctx},
node{other.node},
vtable{other.vtable} {}
public:
/*! Default constructor. */
meta_any() = default;
/**
* @brief Context aware constructor.
* @param area The context from which to search for meta types.
*/
meta_any(meta_ctx_arg_t, const meta_ctx &area)
: ctx{&area} {}
/**
* @brief Constructs a wrapper by directly initializing the new object.
* @tparam Type Type of object to use to initialize the wrapper.
* @param args Parameters to use to construct the instance.
*/
template<typename Type>
explicit meta_any(stl::in_place_type_t<Type>, auto &&...args)
: meta_any{locator<meta_ctx>::value_or(), stl::in_place_type<Type>, stl::forward<decltype(args)>(args)...} {}
/**
* @brief Constructs a wrapper by directly initializing the new object.
* @tparam Type Type of object to use to initialize the wrapper.
* @param area The context from which to search for meta types.
* @param args Parameters to use to construct the instance.
*/
template<typename Type>
explicit meta_any(const meta_ctx &area, stl::in_place_type_t<Type>, auto &&...args)
: storage{stl::in_place_type<Type>, stl::forward<decltype(args)>(args)...},
ctx{&area},
vtable{&basic_vtable<stl::remove_cvref_t<Type>>} {}
/**
* @brief Constructs a wrapper taking ownership of the passed object.
* @param value A pointer to an object to take ownership of.
*/
explicit meta_any(stl::in_place_t, auto *value)
: meta_any{locator<meta_ctx>::value_or(), stl::in_place, value} {}
/**
* @brief Constructs a wrapper taking ownership of the passed object.
* @param area The context from which to search for meta types.
* @param value A pointer to an object to take ownership of.
*/
explicit meta_any(const meta_ctx &area, stl::in_place_t, auto *value)
: storage{stl::in_place, value},
ctx{&area},
vtable{storage ? &basic_vtable<stl::remove_const_t<stl::remove_pointer_t<decltype(value)>>> : nullptr} {
}
/**
* @brief Constructs a wrapper from a given value.
* @param value An instance of an object to use to initialize the wrapper.
*/
meta_any(auto &&value)
requires (!stl::same_as<stl::remove_cvref_t<decltype(value)>, meta_any>)
: meta_any{locator<meta_ctx>::value_or(), stl::forward<decltype(value)>(value)} {}
/**
* @brief Constructs a wrapper from a given value.
* @param area The context from which to search for meta types.
* @param value An instance of an object to use to initialize the wrapper.
*/
meta_any(const meta_ctx &area, auto &&value)
requires (!stl::same_as<stl::remove_cvref_t<decltype(value)>, meta_any>)
: meta_any{area, stl::in_place_type<stl::remove_cvref_t<decltype(value)>>, stl::forward<decltype(value)>(value)} {}
/**
* @brief Context aware copy constructor.
* @param area The context from which to search for meta types.
* @param other The instance to copy from.
*/
meta_any(const meta_ctx &area, const meta_any &other)
: storage{other.storage},
ctx{&area},
node{(ctx == other.ctx) ? other.node : nullptr},
vtable{other.vtable} {}
/**
* @brief Context aware move constructor.
* @param area The context from which to search for meta types.
* @param other The instance to move from.
*/
meta_any(const meta_ctx &area, meta_any &&other)
: storage{stl::move(other.storage)},
ctx{&area},
node{(ctx == other.ctx) ? stl::exchange(other.node, nullptr) : nullptr},
vtable{stl::exchange(other.vtable, nullptr)} {}
/**
* @brief Copy constructor.
* @param other The instance to copy from.
*/
meta_any(const meta_any &other)
: storage{other.storage},
ctx{other.ctx},
node{(other.storage && !storage) ? nullptr : other.node},
vtable{(other.storage && !storage) ? nullptr : other.vtable} {
}
/**
* @brief Move constructor.
* @param other The instance to move from.
*/
meta_any(meta_any &&other) noexcept
: storage{stl::move(other.storage)},
ctx{other.ctx},
node{stl::exchange(other.node, nullptr)},
vtable{stl::exchange(other.vtable, nullptr)} {}
/*! @brief Default destructor. */
~meta_any() = default;
/**
* @brief Copy assignment operator.
* @param other The instance to copy from.
* @return This meta any object.
*/
meta_any &operator=(const meta_any &other) {
if(this != &other) {
ctx = other.ctx;
storage = other.storage;
node = (other.storage && !storage) ? nullptr : other.node;
vtable = (other.storage && !storage) ? nullptr : other.vtable;
}
return *this;
}
/**
* @brief Move assignment operator.
* @param other The instance to move from.
* @return This meta any object.
*/
meta_any &operator=(meta_any &&other) noexcept {
storage = stl::move(other.storage);
ctx = other.ctx;
node = stl::exchange(other.node, nullptr);
vtable = stl::exchange(other.vtable, nullptr);
return *this;
}
/**
* @brief Value assignment operator.
* @param value An instance of an object to use to initialize the wrapper.
* @return This meta any object.
*/
meta_any &operator=(auto &&value)
requires (!stl::same_as<stl::remove_cvref_t<decltype(value)>, meta_any>) {
emplace<stl::remove_cvref_t<decltype(value)>>(stl::forward<decltype(value)>(value));
return *this;
}
/**
* @brief Returns the meta type associated with the contained instance.
* @return The meta type associated with the contained instance.
*/
[[nodiscard]] inline meta_type type() const noexcept;
/**
* @brief Sets a meta type for the contained instance.
* @param alias The meta to use with the contained instance.
*/
inline void type(const meta_type &alias) noexcept;
/**
* @brief Invokes the underlying function, if possible.
* @param id Unique identifier.
* @param args Parameters to use to invoke the function.
* @return A wrapper containing the returned value, if any.
*/
meta_any invoke(id_type id, auto &&...args) const;
/*! @copydoc invoke */
meta_any invoke(id_type id, auto &&...args);
/**
* @brief Sets the value of a given variable.
* @param id Unique identifier.
* @param args Parameters to use to set the underlying variable.
* @return True in case of success, false otherwise.
*/
bool set(id_type id, auto &&...args);
/**
* @brief Gets the value of a given variable.
* @param id Unique identifier.
* @param args Parameters to use to set the underlying variable, if any.
* @return A wrapper containing the value of the underlying variable.
*/
[[nodiscard]] meta_any get(id_type id, auto &&...args) const;
/*! @copydoc get */
[[nodiscard]] meta_any get(id_type id, auto &&...args);
/**
* @brief Tries to cast an instance to a given type.
* @tparam Type Type to which to cast the instance.
* @return A (possibly null) pointer to the contained instance.
*/
template<typename Type>
[[nodiscard]] const Type *try_cast() const {
const auto *elem = any_cast<const Type>(&storage);
return ((elem != nullptr) || !*this) ? elem : static_cast<const Type *>(internal::try_cast(internal::meta_context::from(*ctx), fetch_node(), type_hash<stl::remove_const_t<Type>>::value(), storage.data()));
}
/*! @copydoc try_cast */
template<typename Type>
[[nodiscard]] Type *try_cast() {
return ((storage.policy() == any_policy::cref) && !stl::is_const_v<Type>) ? nullptr : const_cast<Type *>(stl::as_const(*this).try_cast<stl::remove_const_t<Type>>());
}
/**
* @brief Tries to cast an instance to a given type.
* @tparam Type Type to which to cast the instance.
* @return A reference to the contained instance.
*/
template<typename Type>
[[nodiscard]] stl::remove_const_t<Type> cast() const {
auto *const instance = try_cast<stl::remove_reference_t<Type>>();
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
}
/*! @copydoc cast */
template<typename Type>
[[nodiscard]] stl::remove_const_t<Type> cast() {
// forces const on non-reference types to make them work also with wrappers for const references
auto *const instance = try_cast<stl::remove_reference_t<const Type>>();
ENTT_ASSERT(instance, "Invalid instance");
return static_cast<Type>(*instance);
}
/**
* @brief Converts an object in such a way that a given cast becomes viable.
* @param type Meta type to which the cast is requested.
* @return A valid meta object if convertible, an invalid one otherwise.
*/
[[nodiscard]] meta_any allow_cast(const meta_type &type) const;
/**
* @brief Converts an object in such a way that a given cast becomes viable.
* @param type Meta type to which the cast is requested.
* @return True if convertible, false otherwise.
*/
[[nodiscard]] bool allow_cast(const meta_type &type);
/**
* @brief Converts an object in such a way that a given cast becomes viable.
* @tparam Type Type to which the cast is requested.
* @return A valid meta object if convertible, an invalid one otherwise.
*/
template<typename Type>
[[nodiscard]] meta_any allow_cast() const {
if constexpr(!stl::is_reference_v<Type> || stl::is_const_v<stl::remove_reference_t<Type>>) {
if(storage.has_value<stl::remove_cvref_t<Type>>()) {
return as_ref();
} else if(*this) {
if constexpr(stl::is_arithmetic_v<stl::remove_cvref_t<Type>> || stl::is_enum_v<stl::remove_cvref_t<Type>>) {
if(const auto &from = fetch_node(); from.conversion_helper) {
return meta_any{*ctx, static_cast<Type>(from.conversion_helper(nullptr, storage.data()))};
}
}
if(const auto &from = fetch_node(); from.details != nullptr) {
if(const auto *elem = internal::find_member(from.details->conv, entt::type_hash<stl::remove_cvref_t<Type>>::value()); elem != nullptr) {
return elem->conv(*ctx, storage.data());
}
for(auto &&curr: from.details->base) {
if(auto other = curr.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.id == entt::type_hash<stl::remove_cvref_t<Type>>::value()) {
return other;
} else if(auto from_base = stl::as_const(other).template allow_cast<Type>(); from_base) {
return from_base;
}
}
}
}
}
return meta_any{meta_ctx_arg, *ctx};
}
/**
* @brief Converts an object in such a way that a given cast becomes viable.
* @tparam Type Type to which the cast is requested.
* @return True if convertible, false otherwise.
*/
template<typename Type>
[[nodiscard]] bool allow_cast() {
if constexpr(stl::is_reference_v<Type> && !stl::is_const_v<stl::remove_reference_t<Type>>) {
return allow_cast<const stl::remove_reference_t<Type> &>() && (storage.policy() != any_policy::cref);
} else {
if(storage.has_value<stl::remove_cvref_t<Type>>()) {
return true;
} else if(auto other = stl::as_const(*this).allow_cast<stl::remove_cvref_t<Type>>(); other) {
if(other.storage.owner()) {
stl::swap(*this, other);
}
return true;
}
return false;
}
}
/*! @copydoc any::emplace */
template<typename Type>
void emplace(auto &&...args) {
storage.emplace<Type>(stl::forward<decltype(args)>(args)...);
auto *prev = stl::exchange(vtable, &basic_vtable<stl::remove_cvref_t<Type>>);
node = (prev == vtable) ? node : nullptr;
}
/*! @copydoc any::assign */
bool assign(const meta_any &other);
/*! @copydoc any::assign */
bool assign(meta_any &&other);
/*! @copydoc any::reset */
void reset() {
storage.reset();
node = nullptr;
vtable = nullptr;
}
/**
* @brief Returns a sequence container proxy.
* @return A sequence container proxy for the underlying object.
*/
[[nodiscard]] meta_sequence_container as_sequence_container() noexcept {
meta_sequence_container proxy{};
if(*this) { vtable(internal::meta_traits::is_sequence_container, *this, &proxy); }
return proxy;
}
/*! @copydoc as_sequence_container */
[[nodiscard]] meta_sequence_container as_sequence_container() const noexcept {
meta_sequence_container proxy{};
if(*this) { vtable(internal::meta_traits::is_sequence_container, as_ref(), &proxy); }
return proxy;
}
/**
* @brief Returns an associative container proxy.
* @return An associative container proxy for the underlying object.
*/
[[nodiscard]] meta_associative_container as_associative_container() noexcept {
meta_associative_container proxy{};
if(*this) { vtable(internal::meta_traits::is_associative_container, *this, &proxy); }
return proxy;
}
/*! @copydoc as_associative_container */
[[nodiscard]] meta_associative_container as_associative_container() const noexcept {
meta_associative_container proxy{};
if(*this) { vtable(internal::meta_traits::is_associative_container, as_ref(), &proxy); }
return proxy;
}
/**
* @brief Indirection operator for dereferencing opaque objects.
* @return A wrapper that shares a reference to an unmanaged object if the
* wrapped element is dereferenceable, an invalid meta any otherwise.
*/
[[nodiscard]] meta_any operator*() noexcept {
meta_any ret{meta_ctx_arg, *ctx};
if(*this) { vtable(internal::meta_traits::is_pointer, *this, &ret); }
return ret;
}
/*! @copydoc operator* */
[[nodiscard]] meta_any operator*() const noexcept {
meta_any ret{meta_ctx_arg, *ctx};
if(*this) { vtable(internal::meta_traits::is_pointer, as_ref(), &ret); }
return ret;
}
/*! @copydoc any::operator bool */
[[nodiscard]] explicit operator bool() const noexcept {
return !(vtable == nullptr);
}
/*! @copydoc any::operator== */
[[nodiscard]] bool operator==(const meta_any &other) const noexcept {
return (ctx == other.ctx) && (!*this == !other) && (storage == other.storage);
}
/*! @copydoc any::as_ref */
[[nodiscard]] meta_any as_ref() noexcept {
return meta_any{*this, storage.as_ref()};
}
/*! @copydoc any::as_ref */
[[nodiscard]] meta_any as_ref() const noexcept {
return meta_any{*this, storage.as_ref()};
}
/**
* @brief Returns the underlying storage.
* @return The underlyig storage.
*/
[[nodiscard]] const any &base() const noexcept {
return storage;
}
/**
* @brief Returns the underlying meta context.
* @return The underlying meta context.
*/
[[nodiscard]] const meta_ctx &context() const noexcept {
return *ctx;
}
private:
any storage{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
mutable const internal::meta_type_node *node{};
vtable_type *vtable{};
};
/**
* @brief Forwards its argument and avoids copies for lvalue references.
* @param value Parameter to use to construct the instance.
* @param ctx The context from which to search for meta types.
* @return A properly initialized and not necessarily owning wrapper.
*/
[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, auto &&value) {
return meta_any{ctx, stl::in_place_type<decltype(value)>, stl::forward<decltype(value)>(value)};
}
/**
* @brief Forwards its argument and avoids copies for lvalue references.
* @param value Parameter to use to construct the instance.
* @return A properly initialized and not necessarily owning wrapper.
*/
[[nodiscard]] meta_any forward_as_meta(auto &&value) {
return forward_as_meta(locator<meta_ctx>::value_or(), stl::forward<decltype(value)>(value));
}
/*! @brief Opaque pointers to instances of any type. */
class meta_handle {
meta_handle(int, auto &value, auto &&...args)
requires stl::same_as<stl::remove_cvref_t<decltype(value)>, meta_any>
: any{stl::forward<decltype(args)>(args)..., value.as_ref()} {}
meta_handle(char, auto &value, auto &&...args)
: any{stl::forward<decltype(args)>(args)..., stl::in_place_type<decltype(value)>, value} {}
public:
/*! Default constructor. */
meta_handle() = default;
/**
* @brief Creates a handle that points to an unmanaged object.
* @param ctx The context from which to search for meta types.
* @param value An instance of an object to use to initialize the handle.
*/
meta_handle(const meta_ctx &ctx, auto &value)
requires (!stl::same_as<stl::remove_cvref_t<decltype(value)>, meta_handle>)
: meta_handle{0, value, ctx} {}
/**
* @brief Creates a handle that points to an unmanaged object.
* @param value An instance of an object to use to initialize the handle.
*/
meta_handle(auto &value)
requires (!stl::same_as<stl::remove_cvref_t<decltype(value)>, meta_handle>)
: meta_handle{0, value} {}
/**
* @brief Context aware move constructor.
* @param area The context from which to search for meta types.
* @param other The instance to move from.
*/
meta_handle(const meta_ctx &area, meta_handle &&other)
: any{area, stl::move(other.any)} {}
/*! @brief Default copy constructor, deleted on purpose. */
meta_handle(const meta_handle &) = delete;
/*! @brief Default move constructor. */
meta_handle(meta_handle &&) = default;
/*! @brief Default destructor. */
~meta_handle() = default;
/**
* @brief Default copy assignment operator, deleted on purpose.
* @return This meta handle.
*/
meta_handle &operator=(const meta_handle &) = delete;
/**
* @brief Default move assignment operator.
* @return This meta handle.
*/
meta_handle &operator=(meta_handle &&) = default;
/**
* @brief Returns false if a handle is invalid, true otherwise.
* @return False if the handle is invalid, true otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return static_cast<bool>(any);
}
/**
* @brief Access operator for accessing the contained opaque object.
* @return A wrapper that shares a reference to an unmanaged object.
*/
[[nodiscard]] meta_any *operator->() {
return &any;
}
private:
meta_any any{};
};
/*! @brief Opaque wrapper for user defined data of any type. */
struct meta_custom {
/*! @brief Default constructor. */
meta_custom() noexcept = default;
/**
* @brief Basic constructor for meta objects.
* @param curr The underlying node with which to construct the instance.
*/
meta_custom(const internal::meta_custom_node &curr) noexcept
: node{&curr} {}
/**
* @brief Generic conversion operator.
* @tparam Type Type to which conversion is requested.
*/
template<typename Type>
[[nodiscard]] operator Type *() const noexcept {
return ((node != nullptr) && (type_hash<stl::remove_const_t<Type>>::value() == node->id)) ? static_cast<Type *>(node->value.get()) : nullptr;
}
/**
* @brief Generic conversion operator.
* @tparam Type Type to which conversion is requested.
*/
template<typename Type>
[[nodiscard]] operator Type &() const noexcept {
ENTT_ASSERT(static_cast<Type *>(*this) != nullptr, "Invalid type");
return *static_cast<Type *>(node->value.get());
}
private:
const internal::meta_custom_node *node{};
};
/**
* @brief Common opaque wrapper for meta objects.
* @tparam Type Underlying meta node type.
*/
template<typename Type>
struct meta_object: protected internal::basic_meta_object<Type> {
/*! @brief Underlying meta node type. */
using node_type = Type;
/*! @brief Unsigned integer type. */
using size_type = stl::size_t;
/*! @brief Default constructor. */
meta_object() noexcept = default;
/**
* @brief Context aware constructor for meta objects.
* @param area The context from which to search for meta types.
* @param curr The underlying node with which to construct the instance.
*/
meta_object(const meta_ctx &area, const node_type &curr) noexcept
: internal::basic_meta_object<Type>{&curr, &area} {
}
/**
* @brief Returns true if an object is valid, false otherwise.
* @return True if the object is valid, false otherwise.
*/
[[nodiscard]] explicit operator bool() const noexcept {
return (this->node != nullptr);
}
/**
* @brief Checks if two objects refer to the same type.
* @param other The object with which to compare.
* @return True if the objects refer to the same type, false otherwise.
*/
[[nodiscard]] bool operator==(const meta_object &other) const noexcept {
return (this->ctx == other.ctx) && (this->node == other.node);
}
};
/*! @brief Opaque wrapper for data members. */
struct meta_data: meta_object<internal::meta_data_node> {
using meta_object::meta_object;
/**
* @brief Returns the name assigned to a data member, if any.
* @return The name assigned to the data member, if any.
*/
[[nodiscard]] stl::string_view name() const noexcept {
return (node_or_assert().name == nullptr) ? stl::string_view{} : stl::string_view{node_or_assert().name};
}
/**
* @brief Returns the number of arguments of a data member's setter.
* @return The number of arguments accepted by the data member's setter.
*/
[[nodiscard]] size_type set_arity() const noexcept {
return node_or_assert().set_arity;
}
/**
* @brief Returns the number of arguments of a data member's getter.
* @return The number of arguments accepted by the data member's getter.
*/
[[nodiscard]] size_type get_arity() const noexcept {
return node_or_assert().get_arity;
}
/**
* @brief Indicates whether a data member is constant or not.
* @return True if the data member is constant, false otherwise.
*/
[[nodiscard]] bool is_const() const noexcept {
return !!(node_or_assert().traits & internal::meta_traits::is_const);
}
/**
* @brief Indicates whether a data member is static or not.
* @return True if the data member is static, false otherwise.
*/
[[nodiscard]] bool is_static() const noexcept {
return !!(node_or_assert().traits & internal::meta_traits::is_static);
}
/*! @copydoc meta_any::type */
[[nodiscard]] inline meta_type type() const noexcept;
/**
* @brief Sets the value of a given variable.
* @tparam Instance Type of instance to operate on.
* @param instance An instance that fits the underlying type.
* @param args Parameters to use to set the underlying variable.
* @return True in case of success, false otherwise.
*/
template<typename Instance = meta_handle>
// NOLINTNEXTLINE(modernize-use-nodiscard)
bool set(Instance &&instance, auto &&...args) const {
return (sizeof...(args) >= set_arity()) && node_or_assert().set(meta_handle{*ctx, stl::forward<Instance>(instance)}, stl::array<meta_any, sizeof...(args)>{meta_any{*ctx, stl::forward<decltype(args)>(args)}...}.data());
}
/**
* @brief Gets the value of a given variable.
* @tparam Instance Type of instance to operate on.
* @param instance An instance that fits the underlying type.
* @param args Parameters to use to get the underlying variable, if any.
* @return A wrapper containing the value of the underlying variable.
*/
template<typename Instance = meta_handle>
[[nodiscard]] meta_any get(Instance &&instance, auto &&...args) const {
return (sizeof...(args) >= get_arity()) ? node_or_assert().get(meta_handle{*ctx, stl::forward<Instance>(instance)}, stl::array<meta_any, sizeof...(args)>{meta_any{*ctx, stl::forward<decltype(args)>(args)}...}.data()) : meta_any{meta_ctx_arg, *ctx};
}
/**
* @brief Returns the type of the i-th argument of a data member's setter.
* @param index Index of the argument of which to return the type.
* @return The type of the i-th argument of a data member's setter.
*/
[[nodiscard]] inline meta_type set_arg(size_type index) const noexcept;
/**
* @brief Returns the type of the i-th argument of a data member's getter.
* @param index Index of the argument of which to return the type.
* @return The type of the i-th argument of a data member's getter.
*/
[[nodiscard]] inline meta_type get_arg(size_type index) const noexcept;
/**
* @brief Returns all meta traits for a given meta object.
* @tparam Type The type to convert the meta traits to.
* @return The registered meta traits, if any.
*/
template<typename Type>
[[nodiscard]] Type traits() const noexcept {
return internal::meta_to_user_traits<Type>(node_or_assert().traits);
}
/**
* @brief Returns user defined data for a given meta object.
* @return User defined arbitrary data.
*/
[[nodiscard]] meta_custom custom() const noexcept {
return {node_or_assert().custom};
}
};
/*! @brief Opaque wrapper for member functions. */
struct meta_func: meta_object<internal::meta_func_node> {
using meta_object::meta_object;
/**
* @brief Returns the name assigned to a member function, if any.
* @return The name assigned to the member function, if any.
*/
[[nodiscard]] stl::string_view name() const noexcept {
return (node_or_assert().name == nullptr) ? stl::string_view{} : stl::string_view{node_or_assert().name};
}
/**
* @brief Returns the number of arguments accepted by a member function.
* @return The number of arguments accepted by the member function.
*/
[[nodiscard]] size_type arity() const noexcept {
return node_or_assert().arity;
}
/**
* @brief Indicates whether a member function is constant or not.
* @return True if the member function is constant, false otherwise.
*/
[[nodiscard]] bool is_const() const noexcept {
return !!(node_or_assert().traits & internal::meta_traits::is_const);
}
/**
* @brief Indicates whether a member function is static or not.
* @return True if the member function is static, false otherwise.
*/
[[nodiscard]] bool is_static() const noexcept {
return !!(node_or_assert().traits & internal::meta_traits::is_static);
}
/**
* @brief Returns the return type of a member function.
* @return The return type of the member function.
*/
[[nodiscard]] inline meta_type ret() const noexcept;
/**
* @brief Returns the type of the i-th argument of a member function.
* @param index Index of the argument of which to return the type.
* @return The type of the i-th argument of a member function.
*/
[[nodiscard]] inline meta_type arg(size_type index) const noexcept;
/**
* @brief Invokes the underlying function, if possible.
* @tparam Instance Type of instance to operate on.
* @param instance An instance that fits the underlying type.
* @param args Parameters to use to invoke the function.
* @return A wrapper containing the returned value, if any.
*/
template<typename Instance = meta_handle>
// NOLINTNEXTLINE(modernize-use-nodiscard)
meta_any invoke(Instance &&instance, auto &&...args) const {
return (sizeof...(args) == arity()) ? node_or_assert().invoke(meta_handle{*ctx, stl::forward<Instance>(instance)}, stl::array<meta_any, sizeof...(args)>{meta_any{*ctx, stl::forward<decltype(args)>(args)}...}.data()) : meta_any{meta_ctx_arg, *ctx};
}
/*! @copydoc meta_data::traits */
template<typename Type>
[[nodiscard]] Type traits() const noexcept {
return internal::meta_to_user_traits<Type>(node_or_assert().traits);
}
/*! @copydoc meta_data::custom */
[[nodiscard]] meta_custom custom() const noexcept {
return {node_or_assert().custom};
}
/**
* @brief Returns the next overload of a given function, if any.
* @return The next overload of the given function, if any.
*/
[[nodiscard]] meta_func next() const {
return (node_or_assert().next != nullptr) ? meta_func{*ctx, *node_or_assert().next} : meta_func{};
}
};
/*! @brief Opaque wrapper for base types. */
struct meta_base: meta_object<internal::meta_base_node> {
using meta_object::meta_object;
/*! @copydoc meta_any::type */
[[nodiscard]] inline meta_type type() const noexcept;
};
/*! @brief Opaque wrapper for types. */
class meta_type {
friend class meta_any;
[[nodiscard]] const auto &fetch_node() const {
return (node == nullptr) ? internal::resolve<void>(internal::meta_context::from(*ctx)) : *node;
}
[[nodiscard]] auto lookup(meta_handle *const args, const auto sz, [[maybe_unused]] bool constness, auto next) const {
decltype(next()) candidate = nullptr;
size_type same{};
bool ambiguous{};
for(auto curr = next(); curr; curr = next()) {
if constexpr(stl::is_same_v<stl::decay_t<decltype(*curr)>, internal::meta_func_node>) {
if(constness && !(curr->traits & internal::meta_traits::is_const)) {
continue;
}
}
if(curr->arity == sz) {
size_type match{};
size_type pos{};
// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic) - waiting for C++20 (and stl::span)
for(; pos < sz; ++pos) {
const auto other = curr->arg(*ctx, pos);
const auto type = args[pos]->type();
if(const auto &info = other.info(); info == type.info()) {
++match;
} else if(!(type.fetch_node().conversion_helper && other.fetch_node().conversion_helper) && !(type.fetch_node().details && (internal::find_member(type.fetch_node().details->base, info.hash()) || internal::find_member(type.fetch_node().details->conv, info.hash())))) {
break;
}
}
// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic)
if(pos == sz) {
if(!candidate || match > same) {
candidate = curr;
same = match;
ambiguous = false;
} else if(match == same) {
if constexpr(stl::is_same_v<stl::decay_t<decltype(*curr)>, internal::meta_func_node>) {
if(!!(curr->traits & internal::meta_traits::is_const) != !!(candidate->traits & internal::meta_traits::is_const)) {
candidate = !!(candidate->traits & internal::meta_traits::is_const) ? curr : candidate;
ambiguous = false;
continue;
}
}
ambiguous = true;
}
}
}
}
return ambiguous ? nullptr : candidate;
}
public:
/*! @brief Unsigned integer type. */
using size_type = internal::meta_type_node::size_type;
/*! @brief Default constructor. */
meta_type() noexcept = default;
/**
* @brief Context aware constructor for meta objects.
* @param area The context from which to search for meta types.
* @param curr The underlying node with which to construct the instance.
*/
meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept
: node{&curr},
ctx{&area} {}
/**
* @brief Returns the type info object of the underlying type.
* @return The type info object of the underlying type.
*/
[[nodiscard]] const type_info &info() const noexcept {
return *fetch_node().info;
}
/**
* @brief Returns the alias assigned to a type.
* @return The alias assigned to the type.
*/
[[nodiscard]] id_type alias() const noexcept {
return fetch_node().alias;
}
/**
* @brief Returns the name assigned to a type, if any.
* @return The name assigned to the type, if any.
*/
[[nodiscard]] stl::string_view name() const noexcept {
return (fetch_node().name == nullptr) ? stl::string_view{} : stl::string_view{fetch_node().name};
}
/**
* @brief Returns the size of the underlying type if known.
* @return The size of the underlying type if known, 0 otherwise.
*/
[[nodiscard]] size_type size_of() const noexcept {
return fetch_node().size_of;
}
/**
* @brief Checks whether a type refers to an arithmetic type or not.
* @return True if the underlying type is an arithmetic type, false
* otherwise.
*/
[[nodiscard]] bool is_arithmetic() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_arithmetic);
}
/**
* @brief Checks whether a type refers to an integral type or not.
* @return True if the underlying type is an integral type, false otherwise.
*/
[[nodiscard]] bool is_integral() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_integral);
}
/**
* @brief Checks whether a type refers to a signed type or not.
* @return True if the underlying type is a signed type, false otherwise.
*/
[[nodiscard]] bool is_signed() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_signed);
}
/**
* @brief Checks whether a type refers to an array type or not.
* @return True if the underlying type is an array type, false otherwise.
*/
[[nodiscard]] bool is_array() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_array);
}
/**
* @brief Checks whether a type refers to an enum or not.
* @return True if the underlying type is an enum, false otherwise.
*/
[[nodiscard]] bool is_enum() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_enum);
}
/**
* @brief Checks whether a type refers to a class or not.
* @return True if the underlying type is a class, false otherwise.
*/
[[nodiscard]] bool is_class() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_class);
}
/**
* @brief Checks whether a type refers to a pointer or not.
* @return True if the underlying type is a pointer, false otherwise.
*/
[[nodiscard]] bool is_pointer() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_pointer);
}
/**
* @brief Provides the type for which the pointer is defined.
* @return The type for which the pointer is defined or this type if it
* doesn't refer to a pointer type.
*/
[[nodiscard]] meta_type remove_pointer() const noexcept {
return meta_type{*ctx, fetch_node().remove_pointer(internal::meta_context::from(*ctx))};
}
/**
* @brief Checks whether a type is a pointer-like type or not.
* @return True if the underlying type is pointer-like, false otherwise.
*/
[[nodiscard]] bool is_pointer_like() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_pointer_like);
}
/**
* @brief Checks whether a type refers to a sequence container or not.
* @return True if the type is a sequence container, false otherwise.
*/
[[nodiscard]] bool is_sequence_container() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_sequence_container);
}
/**
* @brief Checks whether a type refers to an associative container or not.
* @return True if the type is an associative container, false otherwise.
*/
[[nodiscard]] bool is_associative_container() const noexcept {
return !!(fetch_node().traits & internal::meta_traits::is_associative_container);
}
/**
* @brief Checks whether a type refers to a template specialization or not.
* @return True if the type is a template specialization, false otherwise.
*/
[[nodiscard]] bool is_template_specialization() const noexcept {
return (fetch_node().templ.arity != 0u);
}
/**
* @brief Returns the number of template arguments.
* @return The number of template arguments.
*/
[[nodiscard]] size_type template_arity() const noexcept {
return fetch_node().templ.arity;
}
/**
* @brief Returns a tag for the class template of the underlying type.
* @return The tag for the class template of the underlying type.
*/
[[nodiscard]] meta_type template_type() const noexcept {
return (fetch_node().templ.resolve != nullptr) ? meta_type{*ctx, fetch_node().templ.resolve(internal::meta_context::from(*ctx))} : meta_type{};
}
/**
* @brief Returns the type of the i-th template argument of a type.
* @param index Index of the template argument of which to return the type.
* @return The type of the i-th template argument of a type.
*/
[[nodiscard]] meta_type template_arg(const size_type index) const noexcept {
return index < template_arity() ? meta_type{*ctx, fetch_node().templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{};
}
/**
* @brief Checks if a type supports direct casting to another type.
* @param other The meta type to test for.
* @return True if direct casting is allowed, false otherwise.
*/
[[nodiscard]] bool can_cast(const meta_type &other) const noexcept {
// casting this is UB in all cases but we aren't going to use the resulting pointer, so...
return other && ((*this == other) || (internal::try_cast(internal::meta_context::from(*ctx), fetch_node(), other.fetch_node().info->hash(), this) != nullptr));
}
/**
* @brief Checks whether a type supports conversion to another type.
* @param other The meta type to test for.
* @return True if the conversion is allowed, false otherwise.
*/
[[nodiscard]] bool can_convert(const meta_type &other) const noexcept {
if(const auto &to = other.info().hash(); (info().hash() == to) || ((fetch_node().conversion_helper != nullptr) && (other.is_arithmetic() || other.is_enum()))) {
return true;
} else if(const auto &from = fetch_node(); from.details) {
if(const auto *elem = internal::find_member(from.details->conv, to); elem != nullptr) {
return true;
}
for(auto &&curr: from.details->base) {
if(curr.id == to || meta_type{*ctx, curr.type(internal::meta_context::from(*ctx))}.can_convert(other)) {
return true;
}
}
}
return false;
}
/**
* @brief Returns a range to visit registered top-level base meta types.
* @return An iterable range to visit registered top-level base meta types.
*/
[[nodiscard]] meta_range<meta_base, decltype(internal::meta_type_descriptor::base)::const_iterator> base() const noexcept {
using range_type = meta_range<meta_base, decltype(internal::meta_type_descriptor::base)::const_iterator>;
return fetch_node().details ? range_type{{*ctx, fetch_node().details->base.cbegin()}, {*ctx, fetch_node().details->base.cend()}} : range_type{};
}
/**
* @brief Returns a range to visit registered top-level meta data.
* @return An iterable range to visit registered top-level meta data.
*/
[[nodiscard]] meta_range<meta_data, decltype(internal::meta_type_descriptor::data)::const_iterator> data() const noexcept {
using range_type = meta_range<meta_data, decltype(internal::meta_type_descriptor::data)::const_iterator>;
return fetch_node().details ? range_type{{*ctx, fetch_node().details->data.cbegin()}, {*ctx, fetch_node().details->data.cend()}} : range_type{};
}
/**
* @brief Lookup utility for meta data (bases are also visited).
* @param id Unique identifier.
* @param recursive True for a search in the base classes, false otherwise.
* @return The registered meta data for the given identifier, if any.
*/
[[nodiscard]] meta_data data(const id_type id, const bool recursive = true) const {
const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), fetch_node(), id, recursive);
return (elem != nullptr) ? meta_data{*ctx, *elem} : meta_data{};
}
/**
* @brief Returns a range to visit registered top-level functions.
* @return An iterable range to visit registered top-level functions.
*/
[[nodiscard]] meta_range<meta_func, decltype(internal::meta_type_descriptor::func)::const_iterator> func() const noexcept {
using return_type = meta_range<meta_func, decltype(internal::meta_type_descriptor::func)::const_iterator>;
return fetch_node().details ? return_type{{*ctx, fetch_node().details->func.cbegin()}, {*ctx, fetch_node().details->func.cend()}} : return_type{};
}
/**
* @brief Lookup utility for meta functions (bases are also visited).
* @param id Unique identifier.
* @param recursive True for a search in the base classes, false otherwise.
* @return The registered meta function for the given identifier, if any.
*/
[[nodiscard]] meta_func func(const id_type id, const bool recursive = true) const {
const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), fetch_node(), id, recursive);
return (elem != nullptr) ? meta_func{*ctx, *elem} : meta_func{};
}
/**
* @copybrief construct
* @param args Parameters to use to construct the instance.
* @return A wrapper containing the new instance, if any.
*/
[[nodiscard]] meta_any construct(auto &&...args) const {
if(const auto &ref = fetch_node(); ref.details) {
if(const auto *candidate = lookup(stl::array<meta_handle, sizeof...(args)>{meta_handle{*ctx, args}...}.data(), sizeof...(args), false, [first = ref.details->ctor.cbegin(), last = ref.details->ctor.cend()]() mutable { return first == last ? nullptr : &*(first++); }); candidate) {
return candidate->invoke(*ctx, stl::array<meta_any, sizeof...(args)>{meta_any{*ctx, stl::forward<decltype(args)>(args)}...}.data());
}
}
if(const auto &ref = fetch_node(); (sizeof...(args) == 0u) && (ref.default_constructor != nullptr)) {
return ref.default_constructor(*ctx);
}
return meta_any{meta_ctx_arg, *ctx};
}
/**
* @brief Wraps an opaque element of the underlying type.
* @param elem A valid pointer to an element of the underlying type.
* @param transfer_ownership True to transfer ownership, false otherwise.
* @return A wrapper that references the given instance.
*/
[[nodiscard]] meta_any from_void(void *elem, bool transfer_ownership = false) const {
return ((elem != nullptr) && (fetch_node().from_void != nullptr)) ? fetch_node().from_void(*ctx, elem, transfer_ownership ? elem : nullptr) : meta_any{meta_ctx_arg, *ctx};
}
/**
* @brief Wraps an opaque element of the underlying type.
* @param elem A valid pointer to an element of the underlying type.
* @return A wrapper that references the given instance.
*/
[[nodiscard]] meta_any from_void(const void *elem) const {
return ((elem != nullptr) && (fetch_node().from_void != nullptr)) ? fetch_node().from_void(*ctx, nullptr, elem) : meta_any{meta_ctx_arg, *ctx};
}
/**
* @copybrief invoke
* @param id Unique identifier.
* @tparam Instance Type of instance to operate on.
* @param instance An instance that fits the underlying type.
* @param args Parameters to use to invoke the function.
* @return A wrapper containing the returned value, if any.
*/
template<typename Instance = meta_handle>
// NOLINTNEXTLINE(modernize-use-nodiscard)
meta_any invoke(const id_type id, Instance &&instance, auto &&...args) const {
meta_handle wrapped{*ctx, stl::forward<Instance>(instance)};
if(const auto &ref = fetch_node(); ref.details) {
if(auto *elem = internal::find_member(ref.details->func, id); elem != nullptr) {
if(const auto *candidate = lookup(stl::array<meta_handle, sizeof...(args)>{meta_handle{*ctx, args}...}.data(), sizeof...(args), (wrapped->base().policy() == any_policy::cref), [curr = elem]() mutable { return (curr != nullptr) ? stl::exchange(curr, curr->next.get()) : nullptr; }); candidate) {
return candidate->invoke(stl::move(wrapped), stl::array<meta_any, sizeof...(args)>{meta_any{*ctx, stl::forward<decltype(args)>(args)}...}.data());
}
}
}
for(auto &&curr: base()) {
if(auto elem = curr.second.type().invoke(id, *wrapped.operator->(), stl::forward<decltype(args)>(args)...); elem) {
return elem;
}
}
return meta_any{meta_ctx_arg, *ctx};
}
/**
* @brief Sets the value of a given variable.
* @tparam Instance Type of instance to operate on.
* @param id Unique identifier.
* @param instance An instance that fits the underlying type.
* @param args Parameters to use to set the underlying variable.
* @return True in case of success, false otherwise.
*/
template<typename Instance = meta_handle>
// NOLINTNEXTLINE(modernize-use-nodiscard)
bool set(const id_type id, Instance &&instance, auto &&...args) const {
const auto candidate = data(id);
return candidate && candidate.set(stl::forward<Instance>(instance), stl::forward<decltype(args)>(args)...);
}
/**
* @brief Gets the value of a given variable.
* @tparam Instance Type of instance to operate on.
* @param id Unique identifier.
* @param instance An instance that fits the underlying type.
* @param args Parameters to use to set the underlying variable, if any.
* @return A wrapper containing the value of the underlying variable.
*/
template<typename Instance = meta_handle>
[[nodiscard]] meta_any get(const id_type id, Instance &&instance, auto &&...args) const {
const auto candidate = data(id);
return candidate ? candidate.get(stl::forward<Instance>(instance), stl::forward<decltype(args)>(args)...) : meta_any{meta_ctx_arg, *ctx};
}
/*! @copydoc meta_data::traits */
template<typename Type>
[[nodiscard]] Type traits() const noexcept {
return internal::meta_to_user_traits<Type>(fetch_node().traits);
}
/*! @copydoc meta_data::custom */
[[nodiscard]] meta_custom custom() const noexcept {
return fetch_node().custom;
}
/*! @copydoc meta_data::operator bool */
[[nodiscard]] explicit operator bool() const noexcept {
return (node != nullptr);
}
/*! @copydoc meta_data::operator== */
[[nodiscard]] bool operator==(const meta_type &other) const noexcept {
return (ctx == other.ctx) && (fetch_node().alias == other.fetch_node().alias);
}
private:
mutable const internal::meta_type_node *node{};
const meta_ctx *ctx{&locator<meta_ctx>::value_or()};
};
[[nodiscard]] inline meta_type meta_any::type() const noexcept {
return *this ? meta_type{*ctx, fetch_node()} : meta_type{};
}
inline void meta_any::type(const meta_type &alias) noexcept {
ENTT_ASSERT(storage.info() == alias.info(), "Unexpected type");
node = alias.node;
ctx = alias.ctx;
}
// NOLINTNEXTLINE(modernize-use-nodiscard)
meta_any meta_any::invoke(const id_type id, auto &&...args) const {
return type().invoke(id, *this, stl::forward<decltype(args)>(args)...);
}
meta_any meta_any::invoke(const id_type id, auto &&...args) {
return type().invoke(id, *this, stl::forward<decltype(args)>(args)...);
}
bool meta_any::set(const id_type id, auto &&...args) {
return type().set(id, *this, stl::forward<decltype(args)>(args)...);
}
[[nodiscard]] inline meta_any meta_any::get(const id_type id, auto &&...args) const {
return type().get(id, *this, stl::forward<decltype(args)>(args)...);
}
[[nodiscard]] inline meta_any meta_any::get(const id_type id, auto &&...args) {
return type().get(id, *this, stl::forward<decltype(args)>(args)...);
}
[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const {
if(storage.has_value(type.info())) {
return as_ref();
} else if(*this) {
if(const auto &from = fetch_node(); (from.conversion_helper != nullptr) && (type.is_arithmetic() || type.is_enum())) {
auto other = type.construct();
const auto value = from.conversion_helper(nullptr, storage.data());
other.fetch_node().conversion_helper(other.storage.data(), &value);
return other;
}
if(const auto &from = fetch_node(); from.details) {
if(const auto *elem = internal::find_member(from.details->conv, type.info().hash()); elem != nullptr) {
return elem->conv(*ctx, storage.data());
}
for(auto &&curr: from.details->base) {
if(auto other = curr.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.cast(storage.data())); curr.id == type.info().hash()) {
return other;
} else if(auto from_base = stl::as_const(other).allow_cast(type); from_base) {
return from_base;
}
}
}
}
return meta_any{meta_ctx_arg, *ctx};
}
[[nodiscard]] inline bool meta_any::allow_cast(const meta_type &type) {
if(storage.has_value(type.info())) {
return true;
} else if(auto other = stl::as_const(*this).allow_cast(type); other) {
if(other.storage.owner()) {
stl::swap(*this, other);
}
return true;
}
return false;
}
inline bool meta_any::assign(const meta_any &other) {
if(!storage.assign(other.storage)) {
auto value = other.allow_cast(type());
return storage.assign(value.storage);
}
return true;
}
inline bool meta_any::assign(meta_any &&other) {
return storage.assign(stl::move(other.storage)) || storage.assign(stl::as_const(other).allow_cast(type()).storage);
}
[[nodiscard]] inline meta_type meta_data::type() const noexcept {
return meta_type{*ctx, node_or_assert().type(internal::meta_context::from(*ctx))};
}
[[nodiscard]] inline meta_type meta_data::set_arg(const size_type index) const noexcept {
return index < set_arity() ? node_or_assert().set_arg(*ctx, index) : meta_type{};
}
[[nodiscard]] inline meta_type meta_data::get_arg(const size_type index) const noexcept {
return index < get_arity() ? node_or_assert().get_arg(*ctx, index) : meta_type{};
}
[[nodiscard]] inline meta_type meta_func::ret() const noexcept {
return meta_type{*ctx, node_or_assert().ret(internal::meta_context::from(*ctx))};
}
[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept {
return index < arity() ? node_or_assert().arg(*ctx, index) : meta_type{};
}
[[nodiscard]] inline meta_type meta_base::type() const noexcept {
return meta_type{*ctx, node_or_assert().type(internal::meta_context::from(*ctx))};
}
/*! @cond ENTT_INTERNAL */
class meta_sequence_container::meta_iterator final {
using vtable_type = void(const void *, const stl::ptrdiff_t, meta_any *);
template<typename It>
static void basic_vtable(const void *value, const stl::ptrdiff_t offset, meta_any *other) {
const auto &it = *static_cast<const It *>(value);
other ? other->emplace<decltype(*it)>(*it) : stl::advance(const_cast<It &>(it), offset);
}
public:
using value_type = meta_any;
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::bidirectional_iterator_tag;
meta_iterator() = default;
meta_iterator(const meta_ctx &area, stl::bidirectional_iterator auto iter) noexcept
: ctx{&area},
vtable{&basic_vtable<decltype(iter)>},
handle{iter} {}
meta_iterator &operator++() noexcept {
return vtable(handle.data(), 1, nullptr), *this;
}
meta_iterator operator++(int value) noexcept {
meta_iterator orig = *this;
vtable(handle.data(), ++value, nullptr);
return orig;
}
meta_iterator &operator--() noexcept {
return vtable(handle.data(), -1, nullptr), *this;
}
meta_iterator operator--(int value) noexcept {
meta_iterator orig = *this;
vtable(handle.data(), --value, nullptr);
return orig;
}
[[nodiscard]] reference operator*() const {
reference other{meta_ctx_arg, *ctx};
vtable(handle.data(), 0, &other);
return other;
}
[[nodiscard]] pointer operator->() const {
return operator*();
}
[[nodiscard]] explicit operator bool() const noexcept {
return (vtable != nullptr);
}
[[nodiscard]] bool operator==(const meta_iterator &other) const noexcept {
return handle == other.handle;
}
[[nodiscard]] const any &base() const noexcept {
return handle;
}
private:
const meta_ctx *ctx{};
vtable_type *vtable{};
any handle{};
};
class meta_associative_container::meta_iterator final {
using vtable_type = void(const void *, stl::pair<meta_any, meta_any> *);
template<bool KeyOnly, typename It>
static void basic_vtable(const void *value, stl::pair<meta_any, meta_any> *other) {
if(const auto &it = *static_cast<const It *>(value); other) {
if constexpr(KeyOnly) {
other->first.emplace<decltype(*it)>(*it);
} else {
other->first.emplace<decltype((it->first))>(it->first);
other->second.emplace<decltype((it->second))>(it->second);
}
} else {
++const_cast<It &>(it);
}
}
public:
using value_type = stl::pair<meta_any, meta_any>;
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;
meta_iterator() = default;
template<bool KeyOnly>
meta_iterator(const meta_ctx &area, stl::bool_constant<KeyOnly>, stl::forward_iterator auto iter) noexcept
: ctx{&area},
vtable{&basic_vtable<KeyOnly, decltype(iter)>},
handle{iter} {}
meta_iterator &operator++() noexcept {
return vtable(handle.data(), nullptr), *this;
}
meta_iterator operator++(int) noexcept {
meta_iterator orig = *this;
vtable(handle.data(), nullptr);
return orig;
}
[[nodiscard]] reference operator*() const {
reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}};
vtable(handle.data(), &other);
return other;
}
[[nodiscard]] pointer operator->() const {
return operator*();
}
[[nodiscard]] explicit operator bool() const noexcept {
return (vtable != nullptr);
}
[[nodiscard]] bool operator==(const meta_iterator &other) const noexcept {
return handle == other.handle;
}
private:
const meta_ctx *ctx{};
vtable_type *vtable{};
any handle{};
};
/*! @endcond */
/**
* @brief Returns the meta value type of a container.
* @return The meta value type of the container.
*/
[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept {
return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
}
/**
* @brief Returns the size of a container.
* @return The size of the container.
*/
[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept {
return size_fn(data);
}
/**
* @brief Resizes a container to contain a given number of elements.
* @param sz The new size of the container.
* @return True in case of success, false otherwise.
*/
inline bool meta_sequence_container::resize(const size_type sz) {
return !const_only && resize_fn(const_cast<void *>(data), sz);
}
/**
* @brief Clears the content of a container.
* @return True in case of success, false otherwise.
*/
inline bool meta_sequence_container::clear() {
return !const_only && clear_fn(const_cast<void *>(data));
}
/**
* @brief Reserves storage for at least the given number of elements.
* @param sz The new capacity of the container.
* @return True in case of success, false otherwise.
*/
inline bool meta_sequence_container::reserve(const size_type sz) {
return !const_only && reserve_fn(const_cast<void *>(data), sz);
}
/**
* @brief Returns an iterator to the first element of a container.
* @return An iterator to the first element of the container.
*/
[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() {
return begin_end_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, false);
}
/**
* @brief Returns an iterator that is past the last element of a container.
* @return An iterator that is past the last element of the container.
*/
[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() {
return begin_end_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, true);
}
/**
* @brief Inserts an element at a specified location of a container.
* @param it Iterator before which the element will be inserted.
* @param value Element value to insert.
* @return A possibly invalid iterator to the inserted element.
*/
inline meta_sequence_container::iterator meta_sequence_container::insert(const iterator &it, meta_any value) {
// this abomination is necessary because only on macos value_type and const_reference are different types for stl::vector<bool>
if(const auto &vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) {
const bool is_value_type = (value.type().info() == *vtype.info);
return insert_fn(*ctx, const_cast<void *>(data), is_value_type ? value.base().data() : nullptr, is_value_type ? nullptr : value.base().data(), it);
}
return iterator{};
}
/**
* @brief Removes a given element from a container.
* @param it Iterator to the element to remove.
* @return A possibly invalid iterator following the last removed element.
*/
inline meta_sequence_container::iterator meta_sequence_container::erase(const iterator &it) {
return const_only ? iterator{} : erase_fn(*ctx, const_cast<void *>(data), it);
}
/**
* @brief Returns a reference to the element at a given location of a container.
* @param pos The position of the element to return.
* @return A reference to the requested element properly wrapped.
*/
[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) {
auto it = begin();
it.operator++(static_cast<int>(pos) - 1);
return *it;
}
/**
* @brief Returns false if a proxy is invalid, true otherwise.
* @return False if the proxy is invalid, true otherwise.
*/
[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept {
return (data != nullptr);
}
/**
* @brief Returns the meta key type of a container.
* @return The meta key type of the a container.
*/
[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept {
return (key_type_node != nullptr) ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{};
}
/**
* @brief Returns the meta mapped type of a container.
* @return The meta mapped type of the a container.
*/
[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept {
return (mapped_type_node != nullptr) ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{};
}
/*! @copydoc meta_sequence_container::value_type */
[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept {
return (value_type_node != nullptr) ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{};
}
/*! @copydoc meta_sequence_container::size */
[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept {
return size_fn(data);
}
/*! @copydoc meta_sequence_container::clear */
inline bool meta_associative_container::clear() {
return !const_only && clear_fn(const_cast<void *>(data));
}
/*! @copydoc meta_sequence_container::reserve */
inline bool meta_associative_container::reserve(const size_type sz) {
return !const_only && reserve_fn(const_cast<void *>(data), sz);
}
/*! @copydoc meta_sequence_container::begin */
[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() {
return begin_end_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, false);
}
/*! @copydoc meta_sequence_container::end */
[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() {
return begin_end_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, true);
}
/**
* @brief Inserts a key-only or key/value element into a container.
* @param key The key of the element to insert.
* @param value The value of the element to insert, if needed.
* @return A bool denoting whether the insertion took place.
*/
inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) {
return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})
&& ((mapped_type_node == nullptr) || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))}))
&& insert_fn(const_cast<void *>(data), key.base().data(), value.base().data());
}
/**
* @brief Removes the specified element from a container.
* @param key The key of the element to remove.
* @return A bool denoting whether the removal took place.
*/
inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) {
return (!const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})) ? erase_fn(const_cast<void *>(data), key.base().data()) : 0u;
}
/**
* @brief Returns an iterator to the element with a given key, if any.
* @param key The key of the element to search.
* @return An iterator to the element with the given key, if any.
*/
[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) {
return key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast<void *>(data), data, key.base().data()) : iterator{};
}
/**
* @brief Returns false if a proxy is invalid, true otherwise.
* @return False if the proxy is invalid, true otherwise.
*/
[[nodiscard]] inline meta_associative_container::operator bool() const noexcept {
return (data != nullptr);
}
} // namespace entt
#endif