363 lines
8.0 KiB
C++
363 lines
8.0 KiB
C++
// =====================================================================================================================
|
|
// fennec, a free and open source game engine
|
|
// Copyright © 2025 - 2026 Medusa Slockbower
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
// =====================================================================================================================
|
|
|
|
///
|
|
/// \file fennec/containers/generic.h
|
|
/// \brief
|
|
///
|
|
///
|
|
/// \details
|
|
/// \author Medusa Slockbower
|
|
///
|
|
/// \copyright Copyright © 2025 - 2026 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
|
///
|
|
///
|
|
|
|
#ifndef FENNEC_CONTAINERS_GENERIC_H
|
|
#define FENNEC_CONTAINERS_GENERIC_H
|
|
|
|
#include <fennec/memory/allocator.h>
|
|
#include <fennec/rtti/type.h>
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
///
|
|
/// \brief A struct capable of holding a single object of any type
|
|
/// \details
|
|
/// | Property | Value |
|
|
/// |:-----------:|:----------:|
|
|
/// | stable | ✅ |
|
|
/// | dynamic | ⛔ |
|
|
/// | homogeneous | ⛔ |
|
|
/// | distinct | ⛔ |
|
|
/// | ordered | ⛔ |
|
|
/// | space | \f$O(1)\f$ |
|
|
/// | linear | ✅ |
|
|
/// | access | \f$O(1)\f$ |
|
|
/// | find | ⛔ |
|
|
/// | insertion | \f$O(1)\f$ |
|
|
/// | deletion | \f$O(1)\f$ |
|
|
/// | space | \f$O(1)\f$ |
|
|
struct generic {
|
|
|
|
// Definitions =========================================================================================================
|
|
private:
|
|
|
|
// based on GCC
|
|
enum op_ : uint8_t {
|
|
op_clone,
|
|
op_destroy,
|
|
op_type,
|
|
};
|
|
|
|
using manager_t = void* (*)(uint8_t, void*);
|
|
|
|
|
|
// Constructors & Destructor ===========================================================================================
|
|
public:
|
|
|
|
/// \name Constructors & Destructor
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Default Constructor
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
generic()
|
|
: _handle(nullptr)
|
|
, _manage(nullptr) {
|
|
}
|
|
|
|
///
|
|
/// \brief Copy Constructor
|
|
/// \param gen The generic object to copy
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
generic(const generic& gen)
|
|
: _handle(nullptr)
|
|
, _manage(gen._manage) {
|
|
if (_manage) {
|
|
_handle = _manage(op_clone, gen._handle);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Move Constructor
|
|
/// \param gen The generic object to move
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
generic(generic&& gen)
|
|
: _handle(gen._handle)
|
|
, _manage(gen._manage) {
|
|
gen._handle = nullptr;
|
|
gen._manage = nullptr;
|
|
}
|
|
|
|
///
|
|
/// \brief Value Constructor
|
|
/// \tparam T The type of the value
|
|
/// \param x The value
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
template<typename T>
|
|
generic(T&& x)
|
|
: _handle(new T(fennec::forward<T>(x)))
|
|
, _manage(_manage_impl<T>) {
|
|
}
|
|
|
|
///
|
|
/// \brief Emplace Constructor
|
|
/// \tparam T The type to construct
|
|
/// \tparam ArgsT The argument types
|
|
/// \param args The argument values
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
template<typename T, typename...ArgsT>
|
|
generic(type_identity<T>, ArgsT&&...args)
|
|
: _handle(new T(fennec::forward<ArgsT>(args)...))
|
|
, _manage(_manage_impl<T>) {
|
|
}
|
|
|
|
///
|
|
/// \brief Destructor
|
|
~generic() {
|
|
reset();
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Properties ==========================================================================================================
|
|
public:
|
|
|
|
/// \name Properties
|
|
/// @{
|
|
|
|
///
|
|
/// \brief runtime type acquisition
|
|
/// \returns a runtime type struct referencing the held type
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
type type() const {
|
|
return *static_cast<fennec::type*>(_manage(op_type, nullptr));
|
|
}
|
|
|
|
///
|
|
/// \returns \f$true\f$ if there is a held value, \f$false\f$ otherwise
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
bool has_value() const {
|
|
return _handle != nullptr;
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Assignment ==========================================================================================================
|
|
public:
|
|
|
|
/// \name Assignment Operators
|
|
/// @{
|
|
|
|
///
|
|
/// \brief copy assignment
|
|
/// \param gen the generic to copy
|
|
/// \returns a reference to self after copying the contents of \f$gen\f$
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
generic& operator=(const generic& gen) {
|
|
if (this == &gen) { // self-assignment case
|
|
return *this;
|
|
}
|
|
|
|
reset();
|
|
_manage = gen._manage;
|
|
_handle = _manage(op_clone, gen._handle);
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// \brief move assignment
|
|
/// \param gen the generic to move
|
|
/// \returns a reference to self after swapping contents with \f$gen\f$
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
generic& operator=(generic&& gen) noexcept {
|
|
swap(gen);
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// \brief value assignment
|
|
/// \tparam T the type of the value
|
|
/// \param x the value to assign
|
|
/// \returns a reference to self after having assigned \f$x\f$
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
template<typename T>
|
|
generic& operator=(T&& x) {
|
|
reset();
|
|
_handle = new T(fennec::forward<T>(x));
|
|
_manage = _manage_impl<T>();
|
|
return *this;
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Modifiers ===========================================================================================================
|
|
public:
|
|
|
|
/// \name Modifiers
|
|
/// @{
|
|
|
|
///
|
|
/// \brief emplace value
|
|
///
|
|
/// \details constructs a new value of type \f$T\f$ using \f$args\ldots\f$
|
|
/// \tparam T the type to construct
|
|
/// \tparam ArgsT the argument types
|
|
/// \param args the argument values
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
template<typename T, typename...ArgsT>
|
|
void emplace(ArgsT&&...args) {
|
|
reset();
|
|
_handle = new T(fennec::forward<ArgsT>(args)...);
|
|
_manage = _manage_impl<T>;
|
|
}
|
|
|
|
///
|
|
/// \brief reset value
|
|
/// \details clears the held value using the appropriate destructor
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
void reset() {
|
|
if (_manage) {
|
|
_handle = _manage(op_destroy, _handle);
|
|
_manage = nullptr;
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief C++ 11 Swap Specification
|
|
/// \param gen the generic to swap with
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
void swap(generic& gen) noexcept {
|
|
fennec::swap(_handle, gen._handle);
|
|
fennec::swap(_manage, gen._manage);
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Casting =============================================================================================================
|
|
public:
|
|
|
|
/// \name Casting
|
|
/// @{
|
|
|
|
///
|
|
/// \brief cast value
|
|
///
|
|
/// \details equivalent to \f$reinterpret_cast\f$
|
|
/// \tparam T The type to cast to
|
|
/// \returns The contents of generic after having cast to \f$T\f$
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
template<typename T, typename U = remove_cvref_t<T>>
|
|
T cast() {
|
|
return static_cast<T>(*static_cast<U*>(_handle));
|
|
}
|
|
|
|
///
|
|
/// \details equivalent to \f$reinterpret_cast\f$
|
|
/// \tparam T The type to cast to
|
|
/// \returns The contents of generic after having cast to \f$T\f$
|
|
///
|
|
/// \par Complexity
|
|
/// \f$O(1)\f$
|
|
///
|
|
template<typename T, typename U = remove_cvref_t<T>>
|
|
T cast() const {
|
|
return static_cast<T>(*static_cast<U*>(_handle));
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Private Member Variables ============================================================================================
|
|
private:
|
|
void* _handle;
|
|
manager_t _manage;
|
|
|
|
|
|
// Private Helpers =====================================================================================================
|
|
private:
|
|
template<typename T>
|
|
static void* _manage_impl(uint8_t op, void* hnd) {
|
|
static fennec::type t = type::get<T>();
|
|
T* ptr = hnd;
|
|
|
|
switch (op) {
|
|
case op_clone:
|
|
return new T(*ptr);
|
|
case op_destroy:
|
|
delete ptr;
|
|
return nullptr;
|
|
case op_type:
|
|
return &t;
|
|
default:
|
|
return nullptr;
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif // FENNEC_CONTAINERS_GENERIC_H
|