Files
fennec/include/fennec/containers/generic.h
2026-01-06 19:48:28 -05:00

277 lines
7.1 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$ |
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 ========================================================================================================
public:
///
/// \brief Default Constructor
generic()
: _handle(nullptr)
, _manage(nullptr) {
}
///
/// \brief Copy Constructor
/// \param gen The generic object to copy
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
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
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
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:
///
/// \brief runtime type acquisition
/// \returns a runtime type struct referencing the held type
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
bool has_value() const {
return _handle != nullptr;
}
// Assignment ==========================================================================================================
public:
///
/// \brief copy assignment
/// \param gen the generic to copy
/// \returns a reference to self after copying the contents of \f$gen\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$
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$
template<typename T>
generic& operator=(T&& x) {
reset();
_handle = new T(fennec::forward<T>(x));
_manage = _manage_impl<T>();
return *this;
}
// Insertion & Deletion ================================================================================================
public:
///
/// \brief emplace value
///
/// \details constructs a new value of type \f$T\f$ using \f$args...\f$
/// \tparam T the type to construct
/// \tparam ArgsT the argument types
/// \param args the argument values
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
void reset() {
if (_manage) {
_handle = _manage(op_destroy, _handle);
_manage = nullptr;
}
}
// Utility =============================================================================================================
public:
///
/// \brief C++ 11 Swap Specification
/// \param gen the generic to swap with
void swap(generic& gen) noexcept {
fennec::swap(_handle, gen._handle);
fennec::swap(_manage, gen._manage);
}
///
/// \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$
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$
template<typename T, typename U = remove_cvref_t<T>>
T cast() const {
return static_cast<T>(*static_cast<U*>(_handle));
}
// Members =============================================================================================================
private:
void* _handle;
manager_t _manage;
// 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