Files
Cubed/include/entt/core/any.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

624 lines
22 KiB
C++

#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