- fennec::variant & fennec::generic, TODO: Test

This commit is contained in:
2025-12-13 19:00:43 -05:00
parent 9553f9b662
commit a1bdc077b1
23 changed files with 974 additions and 56 deletions

View File

@@ -113,6 +113,7 @@ add_library(fennec STATIC
include/fennec/containers/bitfield.h
include/fennec/containers/deque.h
include/fennec/containers/dynarray.h
include/fennec/containers/generic.h
include/fennec/containers/graph.h
include/fennec/containers/initializer_list.h
include/fennec/containers/list.h
@@ -176,6 +177,8 @@ add_library(fennec STATIC
include/fennec/rtti/enable.h
include/fennec/rtti/forward.h
include/fennec/rtti/typelist.h
include/fennec/rtti/type_registry.h
include/fennec/rtti/singleton.h
include/fennec/rtti/detail/_constants.h

View File

@@ -27,5 +27,4 @@ fennec_add_definitions(
FENNEC_COMPILER_GCC=1
FENNEC_NO_INLINE=[[gnu::noinline]]
FENNEC_FUNCTION_NAME=__PRETTY_FUNCTION__
RYU_ONLY_64_BIT_OPS
)

View File

@@ -0,0 +1,210 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 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 universal.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 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
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 ==========================================================================================================
type type() const {
return *static_cast<fennec::type*>(_manage(op_type, nullptr));
}
bool has_value() const {
return _handle != nullptr;
}
// Assignment ==========================================================================================================
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;
}
generic& operator=(generic&& gen) noexcept {
swap(gen);
return *this;
}
// Utility =============================================================================================================
template<typename T>
generic& operator=(T&& x) {
reset();
_handle = new T(fennec::forward<T>(x));
_manage = _manage_impl<T>();
return *this;
}
template<typename T, typename...ArgsT>
void emplace(ArgsT&&...args) {
reset();
_handle = new T(fennec::forward<ArgsT>(args)...);
_manage = _manage_impl<T>;
}
void reset() {
if (_manage) {
_handle = _manage(op_destroy, _handle);
_manage = nullptr;
}
}
void swap(generic& gen) noexcept {
fennec::swap(_handle, gen._handle);
fennec::swap(_manage, gen._manage);
}
template<typename T>
T& cast() {
return *static_cast<T*>(_handle);
}
template<typename T>
const T& cast() const {
return *static_cast<T*>(_handle);
}
private:
void* _handle;
manager_t _manage;
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

View File

@@ -31,4 +31,216 @@
#ifndef FENNEC_CONTAINERS_VARIANT_H
#define FENNEC_CONTAINERS_VARIANT_H
#include <fennec/containers/optional.h>
#include <fennec/lang/type_sequences.h>
#include <fennec/math/ext/common.h>
#include <fennec/rtti/type.h>
namespace fennec
{
///
/// \brief A structure that represents a union between `TypesT...`
/// \tparam TypesT The types to hold in the variant
template<typename...TypesT>
struct variant {
// Assertions ==========================================================================================================
static_assert(
is_unique_v<TypesT...> and // No two types in TypesT... may be equivalent
not (is_reference_v<TypesT> or ...) and // No type in TypesT... may be a reference
not (is_array_v<TypesT> or ...) and // No type in TypesT... may be an array
not (is_void_v<TypesT> or ...) // No type in TypesT... may be void
);
// Typedefs & Constants ================================================================================================
static constexpr size_t size = max_element_size_v<TypesT...>;
static constexpr size_t nulltype = sizeof...(TypesT);
// Constructors ========================================================================================================
///
/// \brief Default Constructor, constructs the first type in `TypesT...` that is default constructible
variant()
: _bytes {}
, _type(nulltype) {
using construct_t = search_element_t<is_default_constructible, TypesT...>;
fennec::construct<construct_t>(_handle);
}
///
/// \brief Conversion Constructor, constructs the type in `TypesT...` that is identical to `T`
/// or the first that is constructible with `T`
/// \tparam T The type of the value
/// \param t The value to forward
template<typename T>
variant(T&& t)
: _bytes {}
, _type() {
using same_t = search_element_args<is_same, type_sequence<T>, TypesT...>;
using convert_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>;
using construct_t = conditional_t<is_void_v<same_t>, convert_t, convert_t>;
fennec::construct<construct_t>(_handle, fennec::forward<T>(t));
}
///
/// \brief Emplace Constructor, constructs the first type in `TypesT...` that is constructible with `ArgsT...`
/// \tparam ArgsT The arguments of the constructor
/// \param args The argument values
template<typename T, typename...ArgsT>
variant(type_identity<T>, ArgsT&&...args)
: _bytes{}
, _type(nulltype) {
static_assert(contains_element_v<T, TypesT...>, "T must be in TypesT...");
fennec::construct<T>(_handle, fennec::forward<ArgsT>(args)...);
_type = find_element_v<T>;
}
///
/// \brief Copy Constructor
/// \param v The variant to copy
variant(const variant& v)
: _bytes {}
, _type(nulltype) {
if (v._type == nulltype) {
return;
}
((v._type == find_element_v<TypesT, TypesT...> ?
fennec::construct<TypesT>(_handle, v.get<TypesT>()) :
(0)
), ...);
_type = v._type;
}
///
/// \brief Move Constructor
/// \param v The variant to move
variant(variant&& v) noexcept
: _bytes {}
, _type() {
if (v._type == nulltype) {
return;
}
((v._type == find_element_v<TypesT, TypesT...> ?
fennec::construct<TypesT>(_handle, fennec::move(v.get<TypesT>())) :
(0)
), ...);
_type = v._type;
}
///
/// \brief Destructor, if a type is held, destruct it.
~variant() {
_clear();
}
// Assignment ==========================================================================================================
template<typename T>
variant& operator=(T&& t) {
// First, check if `T` is in `TypesT...`
if constexpr((contains_element_v<T, TypesT> or ...)) {
using type_t = remove_reference_t<T>;
if (_type == find_element_v<type_t, TypesT...>) {
*static_cast<type_t*>(_handle) = fennec::forward<T>(t);
} else {
_clear();
fennec::construct<type_t>(_handle, fennec::forward<T>(t));
_type = find_element_v<type_t, TypesT...>;
}
return *this;
}
// Next, try to assign using the currently held type
bool assigned = false;
if (_type != nulltype) {
((_type == find_element_v<TypesT, TypesT...> ?
(*static_cast<TypesT*>(_handle) = fennec::forward<T>(t), assigned = true) :
(0)
), ...);
}
if (assigned) {
return *this;
}
// Otherwise, destruct, then construct
_clear();
using construct_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>;
fennec::construct<construct_t>(_handle, fennec::forward<T>(t));
return *this;
}
template<typename T, typename...ArgsT> requires(contains_element_v<T, TypesT...>)
void emplace(ArgsT&&...args) {
_clear();
fennec::construct<T>(_handle, fennec::forward<ArgsT>(args)...);
}
template<size_t I, typename...ArgsT>
void emplace(ArgsT&&...args) {
using type_t = nth_element_t<I, TypesT...>;
_clear();
fennec::construct<type_t>(fennec::forward<ArgsT>(args)...);
}
// Access ==============================================================================================================
template<typename T> requires(contains_element_v<T, TypesT...>)
T& get() {
return *static_cast<T*>(_handle);
}
template<typename T> requires(contains_element_v<T, TypesT...>)
const T& get() const {
return *static_cast<T*>(_handle);
}
template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
T& get() {
return *static_cast<T*>(_handle);
}
template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
const T& get() const {
return *static_cast<T*>(_handle);
}
private:
union {
byte_t _bytes[size];
void* _handle;
};
size_t _type;
void _clear() {
if (_type == nulltype) {
return;
}
((_type == find_element_v<TypesT, TypesT...> ?
fennec::destruct<TypesT>(_handle) :
(0)
), ...);
_type = nulltype;
}
};
}
#endif // FENNEC_CONTAINERS_VARIANT_H

View File

@@ -19,13 +19,19 @@
#ifndef FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
#define FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H
#include <fennec/math/common.h>
#include <fennec/lang/type_transforms.h>
namespace fennec::detail
{
template<typename...TypesT> struct _type_sequence {};
template<typename FirstT, typename... RestT> struct _first_element : type_identity<FirstT> {};
// fennec::nth_element =================================================================================================
template<size_t n, size_t i, typename...TypesT> struct _nth_element;
template<size_t n, size_t i> struct _nth_element<n, i> : type_identity<void> {};
@@ -35,6 +41,56 @@ namespace fennec::detail
n == i, HeadT,
typename _nth_element<n, i + 1, RestT...>::type
> {};
// fennec::max_element_size ============================================================================================
template<size_t, typename...> struct _max_element_size;
template<size_t M, typename HeadT>
struct _max_element_size<M, HeadT>
: integral_constant<size_t, fennec::max(M, sizeof(HeadT))> {
};
template<size_t M, typename HeadT, typename...RestT>
struct _max_element_size<M, HeadT, RestT...>
: _max_element_size<fennec::max(M, sizeof(HeadT)), RestT...> {
};
// fennec::find_element ================================================================================================
template<size_t N, typename, typename...> struct _find_element;
template<size_t N, typename FindT, typename HeadT>
struct _find_element<N, FindT, HeadT> : integral_constant<size_t, is_same_v<FindT, HeadT> ? N : N + 1> {};
template<size_t N, typename FindT, typename HeadT, typename...RestT> requires(is_same_v<FindT, HeadT>)
struct _find_element<N, FindT, HeadT, RestT...>
: conditional_t<is_same_v<FindT, HeadT>, integral_constant<size_t, N>, _find_element<N + 1, FindT, RestT...>> {};
// fennec::search_element ==============================================================================================
template<template<typename> typename, typename...> struct _search_element;
template<template<typename> typename SearchT> struct _search_element<SearchT> : type_identity<void> {};
template<template<typename> typename SearchT, typename HeadT, typename...RestT> requires(SearchT<HeadT>{})
struct _search_element<SearchT, HeadT, RestT...>
: conditional_t<SearchT<HeadT>{}, type_identity<HeadT>, _search_element<SearchT, RestT...>> {
};
template<template<typename, typename...> typename, typename, typename...> struct _search_element_args;
template<template<typename, typename...> typename SearchT, typename...ArgsT>
struct _search_element_args<SearchT, _type_sequence<ArgsT...>> : type_identity<void> {};
template<template<typename, typename...> typename SearchT, typename HeadT, typename...RestT, typename...ArgsT>
struct _search_element_args<SearchT, _type_sequence<ArgsT...>, HeadT, RestT...>
: conditional_t<SearchT<HeadT, ArgsT...>{}, type_identity<HeadT>, _search_element_args<SearchT, _type_sequence<ArgsT...>, RestT...>> {};
}
#endif // FENNEC_LANG_DETAIL_TYPE_SEQUENCES_H

View File

@@ -66,6 +66,16 @@ namespace fennec::detail
template<typename> struct _is_pointer : false_type {};
template<typename T> struct _is_pointer<T*> : true_type {};
template<typename> struct _is_reference : false_type {};
template<typename T> struct _is_reference<T&> : true_type {};
template<typename T> struct _is_reference<T&&> : true_type {};
template<typename> struct _is_lvalue_reference : false_type {};
template<typename T> struct _is_lvalue_reference<T&> : true_type {};
template<typename> struct _is_rvalue_reference : false_type {};
template<typename T> struct _is_rvalue_reference<T&&> : true_type {};
template<typename T> struct _is_complete {
template<typename U>
static auto test(U*) -> bool_constant<sizeof(U) == sizeof(U)>;

View File

@@ -213,6 +213,14 @@
# define FENNEC_HAS_BUILTIN_IS_ENUM 0
#endif
// Inconsistent without intrinsics.
#if __has_builtin(__is_union)
# define FENNEC_HAS_BUILTIN_IS_UNION 1
# define FENNEC_BUILTIN_IS_UNION(arg) __is_union(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_UNION 0
#endif
// Inconsistent without intrinsics
#if __has_builtin(__is_final)
# define FENNEC_HAS_BUILTIN_IS_FINAL 1
@@ -221,12 +229,20 @@
# define FENNEC_HAS_BUILTIN_IS_FINAL 0
#endif
// Inconsistent with dynamic intrinsics, requires a massive table for static intrinsics
#if __has_builtin(__is_fundamental)
# define FENNEC_HAS_BUILTIN_IS_FUNDAMENTAL 1
# define FENNEC_BUILTIN_IS_FUNDAMENTAL(arg) __is_fundamental(arg)
// Inconsistent without intrinsics
#if __has_builtin(__is_function)
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 1
# define FENNEC_BUILTIN_IS_FUNCTION(arg) __is_function(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_FUNDAMENTAL 0
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 0
#endif
// Inconsistent without intrinsics
#if __has_builtin(__is_object)
# define FENNEC_HAS_BUILTIN_IS_OBJECT 1
# define FENNEC_BUILTIN_IS_OBJECT(arg) __is_object(arg)
#else
# define FENNEC_HAS_BUILTIN_IS_FUNCTION 0
#endif
// Inconsistent without intrinsics

View File

@@ -43,6 +43,7 @@
/// - \subpage fennec_lang_conditional_types
/// - \subpage fennec_lang_numeric_transforms
/// - \subpage fennec_lang_metasequences
/// - \subpage fennec_lang_type_identity
/// - \subpage fennec_lang_type_sequences
/// - \subpage fennec_lang_type_traits
/// - \subpage fennec_lang_type_transforms

View File

@@ -51,6 +51,12 @@
/// \ref fennec::replace_first_element "typename replace_first_element<ClassT, SubT, OriginT, RestT...>::type"<br>
/// <td width="50%" style="vertical-align: top">
/// \copydoc fennec::replace_first_element
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::is_unique "is_unique<TypesT...>::value"<br>
/// \ref fennec::is_unique_v "is_unique_v<TypesT...>"
/// <td width="50%" style="vertical-align: top">
/// \copydoc fennec::is_unique
/// </table>
///
@@ -59,6 +65,12 @@
namespace fennec
{
template<typename...TypesT> struct type_sequence {};
// fennec::first_element ===============================================================================================
///
/// \brief Get the first element of a template parameter pack
/// \tparam TypesT the Parameter Pack
@@ -69,8 +81,20 @@ template<typename...TypesT> struct first_element : detail::_first_element<TypesT
template<typename...TypesT> using first_element_t = typename first_element<TypesT...>::type;
// fennec::nth_element =================================================================================================
///
/// \brief Gets the type of the nth element of the type sequence `TypesT...`
/// \tparam n The index in the type sequence
/// \tparam TypesT The type sequence
template<size_t n, typename...TypesT> struct nth_element : detail::_nth_element<n, 0, TypesT...> {};
template<size_t n, typename...TypesT> using nth_element_t = nth_element<n, TypesT...>::type;
// fennec::replace_first_element =======================================================================================
///
/// \brief Take a Template with a Pack `ClassT<ArgsT...>` and replace the first `ArgT` of `ArgsT...` with `SubT`
@@ -84,6 +108,99 @@ template<
struct replace_first_element<ClassT<OriginT, RestT...>, SubT> // Specialization
{ using type = ClassT<SubT, RestT...>; }; // Definition
// fennec::max_element_size ============================================================================================
///
/// \brief Gets the max value of the size of each type in the sequence, i.e. `max(sizeof(Ts)...)`
/// \tparam Ts The type sequence to check
template<typename...Ts> struct max_element_size : detail::_max_element_size<0, Ts...> {};
///
/// \brief Shorthand for `max_element_size<Ts...>::value`
/// \tparam Ts The type sequence to check
template<typename...Ts> constexpr size_t max_element_size_v = max_element_size<Ts...>::value;
// fennec::find_element ================================================================================================
///
/// \brief Finds the index of `T` in `Ts`, if `T` is not found, results in `sizeof...(Ts)`
/// \tparam T The type to find
/// \tparam Ts The type sequence to check
template<typename T, typename...Ts> struct find_element : detail::_find_element<0, T, Ts...> {};
///
/// \brief Shorthand for `find_element<T, Ts...>::value`
/// \tparam T The type to find
/// \tparam Ts The type sequence to check
template<typename T, typename...Ts> constexpr size_t find_element_v = find_element<T, Ts...>::value;
// fennec::search_element ==============================================================================================
///
/// \brief Find the first element in `TypesT...` that satisfies `SearchT<T>`
/// \tparam SearchT A type that satisfies `template<typename>` and contains `static constexpr bool value;` to use for searching
/// \tparam TypesT The type sequence to search
template<template<typename> typename SearchT, typename...TypesT> struct search_element : detail::_search_element<SearchT, TypesT...> {};
///
/// \brief Shorthand for `search_element_t<T, Ts...>::type`
/// \tparam SearchT A type that satisfies `template<typename>` and contains `static constexpr bool value;` to use for searching
/// \tparam TypesT The type sequence to search
template<template<typename> typename SearchT, typename...TypesT> using search_element_t = search_element<SearchT, TypesT...>::type;
template<template<typename, typename...> typename, typename, typename...> struct search_element_args;
template<template<typename, typename...> typename SearchT, typename...TypesT, typename...ArgsT>
struct search_element_args<SearchT, type_sequence<ArgsT...>, TypesT...>
: detail::_search_element_args<SearchT, detail::_type_sequence<ArgsT...>, TypesT...> {
};
// fennec::contains_element ============================================================================================
///
/// \brief Checks if the type sequence `Ts...` contains `T`
/// \tparam T The type to find
/// \tparam Ts The type sequence to check
template<typename T, typename...Ts> struct contains_element : bool_constant<(is_same_v<T, Ts> or ...)> {};
///
/// \brief Shorthand for `contains_element_v<T, Ts...>::value`
/// \tparam T The type to find
/// \tparam Ts The type sequence to check
template<typename T, typename...Ts> constexpr bool contains_element_v = contains_element<T, Ts...>::value;
// fennec::is_unique ===================================================================================================
///
/// \brief Checks if all types in a type sequence are unique
/// \tparam Ts The type sequence to check
template<typename...Ts> struct is_unique : false_type {};
// Single type case
template<typename T> struct is_unique<T> : true_type {};
// Recursion case
template<typename T, typename...Ts> requires(not is_same_v<T, Ts> && ...)
struct is_unique<T, Ts...> : is_unique<Ts...> {};
///
/// \brief Shorthand for `is_unique<Ts...>::value`
/// \tparam Ts The type sequence to check
template<typename...Ts> constexpr bool is_unique_v = is_unique<Ts...>::value;
}

View File

@@ -360,7 +360,7 @@
/// \ref fennec::is_nothrow_move_constructible "is_nothrow_move_constructible<ClassT, ArgsT...>::value"<br>
/// \ref fennec::is_nothrow_move_constructible_v "is_nothrow_move_constructible_v<ClassT, ArgsT...>"
/// <td width="50%" style="vertical-align: top">
/// \movedetails fennec::is_move_constructible
/// \copydetails fennec::is_move_constructible
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::is_assignable "is_assignable<ClassT, ArgsT...>::value"<br>
@@ -390,7 +390,7 @@
/// \ref fennec::is_nothrow_move_assignable "is_nothrow_move_assignable<ClassT, ArgsT...>::value"<br>
/// \ref fennec::is_nothrow_move_assignable_v "is_nothrow_move_assignable_v<ClassT, ArgsT...>"
/// <td width="50%" style="vertical-align: top">
/// \movedetails fennec::is_move_assignable
/// \copydetails fennec::is_move_assignable
///
/// <tr><td width="50%" style="vertical-align: top"> <br>
/// \ref fennec::is_destructible "is_destructible<ClassT, ArgsT...>::value"<br>
@@ -446,7 +446,7 @@ constexpr inline bool is_constant_evaluated() noexcept {
// fennec::is_void =====================================================================================================
///
/// \brief check if \p T is of type void
/// \brief Check if \p T is of type void
///
/// \details Stores a boolean value in `is_void::value`, representing whether the provided type is of base type void.
/// \tparam T type to check
@@ -454,7 +454,7 @@ template<typename T> struct is_void
: detail::_is_void<remove_cvr_t<T>>{};
///
/// \brief shorthand for ```is_void<T>::value```
/// \brief Shorthand for ```is_void<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_void_v = is_void<T>::value;
@@ -463,7 +463,7 @@ template<typename T> constexpr bool_t is_void_v = is_void<T>::value;
// fennec::is_null_pointer =============================================================================================
///
/// \brief check if \p T is of type nullptr_t
/// \brief Check if \p T is of type nullptr_t
///
/// \details Stores a boolean value in `is_null_pointer::value`, representing whether the provided type is of base type nullptr_t.
/// \tparam T type to check
@@ -471,7 +471,7 @@ template<typename T> struct is_null_pointer
: detail::_is_null_pointer<remove_cvr_t<T>>{};
///
/// \brief shorthand for ```is_null_pointer<T>::value```
/// \brief Shorthand for ```is_null_pointer<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_null_pointer_v = is_null_pointer<T>::value;
@@ -482,7 +482,7 @@ template<typename T> constexpr bool_t is_null_pointer_v = is_null_pointer<T>::va
#ifdef FENNEC_BUILTIN_IS_ARRAY
///
/// \brief check if \p T is of an array type
/// \brief Check if \p T is of an array type
/// \tparam T type to check
template<typename T> struct is_array
: bool_constant<FENNEC_BUILTIN_IS_ARRAY(T)> {};
@@ -490,7 +490,7 @@ template<typename T> struct is_array
#else
///
/// \brief check if \p T is of an array type
/// \brief Check if \p T is of an array type
/// \tparam T type to check
template<typename T> struct is_array
: false_type {};
@@ -506,25 +506,72 @@ template<typename T> struct is_array<T[]>
#endif
///
/// \brief shorthand for ```is_array<T>::value```
/// \brief Shorthand for ```is_array<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_array_v = is_array<T>::value;
// fennec::is_enum ====================================================================================================
///
/// \brief Check if \p T is an enum
/// \tparam T type to check
template<typename T> struct is_enum
: bool_constant<FENNEC_BUILTIN_IS_ENUM(T)> {};
///
/// \brief Check if \p T is a class
/// \tparam T type to check
template<typename T> constexpr size_t is_enum_v = is_enum<T>::value;
// fennec::is_union ====================================================================================================
///
/// \brief Check if \p T is a union
/// \tparam T type to check
template<typename T> struct is_union
: bool_constant<FENNEC_BUILTIN_IS_UNION(T)> {};
///
/// \brief Check if \p T is a class
/// \tparam T type to check
template<typename T> constexpr size_t is_union_v = is_union<T>::value;
// fennec::is_class ====================================================================================================
///
/// \brief check if \p T is a class
/// \brief Check if \p T is a class
/// \tparam T type to check
template<typename T> struct is_class
: bool_constant<FENNEC_BUILTIN_IS_CLASS(T)> {};
///
/// \brief check if \p T is a class
/// \brief Check if \p T is a class
/// \tparam T type to check
template<typename T> constexpr size_t is_class_v = is_class<T>::value;
// fennec::is_function ====================================================================================================
///
/// \brief Check if \p T is a class
/// \tparam T type to check
template<typename T> struct is_function
: bool_constant<FENNEC_BUILTIN_IS_FUNCTION(T)> {};
///
/// \brief Check if \p T is a class
/// \tparam T type to check
template<typename T> constexpr size_t is_function_v = is_function<T>::value;
// Integral Types ======================================================================================================
@@ -532,7 +579,7 @@ template<typename T> constexpr size_t is_class_v = is_class<T>::value;
// fennec::is_bool =====================================================================================================
///
/// \brief check if \p T is of type bool
/// \brief Check if \p T is of type bool
///
/// \details Stores a boolean value in `is_bool::value`, representing whether the provided type is of base type bool.
/// \tparam T type to check
@@ -540,12 +587,16 @@ template<typename T> struct is_bool
: detail::_is_bool<remove_cvr_t<T>>{};
///
/// \brief shorthand for ```is_bool<T>::value```
/// \brief Shorthand for ```is_bool<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_bool_v = is_bool<T>::value;
// fennec::is_integral =================================================================================================
///
/// \brief check if \p T is of an integral
/// \brief Check if \p T is of an integral
///
/// \details Stores a boolean value in `is_integral::value`, representing whether the provided type is of a base integer type.
/// \tparam T type to check
@@ -553,13 +604,17 @@ template<typename T> struct is_integral
: detail::_is_integral<remove_cvr_t<T>> {};
///
/// \brief shorthand for ```is_integral<T>::value```
/// \brief Shorthand for ```is_integral<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_integral_v = is_integral<T>::value;
// fennec::is_signed =================================================================================================
///
/// \brief check if \p T is of a signed integral
/// \brief Check if \p T is of a signed integral
///
/// \details Checks if type `T` is a signed type i.e. `T(-1) < T(0)` and stores it in `is_same::value`.
/// \tparam T type to check
@@ -567,13 +622,17 @@ template<typename T> struct is_signed
: detail::_is_signed<remove_cvr_t<T>> {};
///
/// \brief shorthand for ```is_signed<T>::value```
/// \brief Shorthand for ```is_signed<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_signed_v = is_signed<T>::value;
// fennec::is_unsigned =================================================================================================
///
/// \brief check if \p T is of an unsigned integral
/// \brief Check if \p T is of an unsigned integral
///
/// \details Checks if type `T` is an unsigned type i.e. `T(-1) > T(0)` and stores it in `is_same::value`.
/// \tparam T type to check
@@ -581,7 +640,7 @@ template<typename T> struct is_unsigned
: detail::_is_unsigned<remove_cvr_t<T>> {};
///
/// \brief shorthand for ```is_unsigned<T>::value```
/// \brief Shorthand for ```is_unsigned<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_unsigned_v = is_unsigned<T>::value;
@@ -590,15 +649,15 @@ template<typename T> constexpr bool_t is_unsigned_v = is_unsigned<T>::value;
// Floating Point Types ================================================================================================
///
/// \brief check if \p T is of a floating point type
/// \brief Check if \p T is of a floating point type
///
/// \details Checks if type `T` is a floating point type and store it in `is_same::value`.
/// \details Stores a boolean value in `is_floating_point::value`, representing whether the provided type is of a base floating point type.
/// \tparam T type to check
template<typename T> struct is_floating_point
: detail::_is_floating_point<remove_cvr_t<T>>{};
///
/// \brief shorthand for ```is_floating_point<T>::value```
/// \brief Shorthand for ```is_floating_point<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_floating_point_v = is_floating_point<T> {};
@@ -606,24 +665,69 @@ template<typename T> constexpr bool_t is_floating_point_v = is_floating_point<T>
// Pointer Types =======================================================================================================
///
/// \brief check if \p T is of a floating point type
/// \brief Check if \p T is of a pointer type
///
/// \details Checks if type `T` is a floating point type and store it in `is_same::value`.
/// \details Stores a boolean value in `is_pointer::value`, representing whether the provided type is of a base pointer type.
/// \tparam T type to check
template<typename T> struct is_pointer
: detail::_is_pointer<remove_cvr_t<T>>{};
///
/// \brief shorthand for ```is_floating_point<T>::value```
/// \brief Shorthand for ```is_pointer<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_pointer_v = is_pointer<T> {};
// Reference Types =======================================================================================================
///
/// \brief Check if \p T is of a floating point type
///
/// \details Checks if type `T` is a floating point type and store it in `is_same::value`.
/// \tparam T type to check
template<typename T> struct is_reference
: detail::_is_reference<T>{};
///
/// \brief Shorthand for ```is_floating_point<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_reference_v = is_reference<T> {};
///
/// \brief Check if \p T is of a floating point type
///
/// \details Checks if type `T` is a floating point type and store it in `is_same::value`.
/// \tparam T type to check
template<typename T> struct is_lvalue_reference
: detail::_is_lvalue_reference<T>{};
///
/// \brief Shorthand for ```is_floating_point<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_lvalue_reference_v = is_lvalue_reference<T> {};
///
/// \brief Check if \p T is of a floating point type
///
/// \details Checks if type `T` is a floating point type and store it in `is_same::value`.
/// \tparam T type to check
template<typename T> struct is_rvalue_reference
: detail::_is_rvalue_reference<T>{};
///
/// \brief Shorthand for ```is_floating_point<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_rvalue_reference_v = is_rvalue_reference<T> {};
// Arithmetic Types ====================================================================================================
///
/// \brief check if \p T is an arithmetic type
/// \brief Check if \p T is an arithmetic type
///
/// \details Checks if type `T` is a built-in type with arithmetic operators and store it in `is_same::value`.
/// \tparam T type to check
@@ -631,21 +735,38 @@ template<typename T> struct is_arithmetic
: bool_constant<is_integral_v<T> or is_floating_point_v<T>>{};
///
/// \brief shorthand for ```is_arithmetic<T>::value```
/// \brief Shorthand for ```is_arithmetic<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_arithmetic_v = is_arithmetic<T>::value;
// Arithmetic Types ====================================================================================================
///
/// \brief Check if \p T is an arithmetic type
///
/// \details Checks if type `T` is a built-in type with arithmetic operators and store it in `is_same::value`.
/// \tparam T type to check
template<typename T> struct is_scalar
: bool_constant<is_arithmetic_v<T> or is_enum_v<T> or is_pointer_v<T>>{};
///
/// \brief Shorthand for ```is_scalar<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_scalar_v = is_scalar<T>::value;
// fennec::is_fundamental ==============================================================================================
///
/// \brief check if \p T is a fundamental type, i.e. arithmetic, void, or nullptr_t
/// \brief Check if \p T is a fundamental type, i.e. arithmetic, void, or nullptr_t
/// \tparam T type to check
template<typename T> struct is_fundamental
: bool_constant<is_arithmetic_v<T> || is_void_v<T> || is_null_pointer_v<T>>{};
///
/// \brief shorthand for ```is_fundamental<T>::value```
/// \brief Shorthand for ```is_fundamental<T>::value```
/// \tparam T type to check
template<typename T> constexpr bool_t is_fundamental_v = is_fundamental<T>::value;
@@ -653,7 +774,7 @@ template<typename T> constexpr bool_t is_fundamental_v = is_fundamental<T>::valu
// fennec::is_same =====================================================================================================
///
/// \brief check if the two types are identical
/// \brief Check if the two types are identical
///
/// \details Checks if `T0` and `T1` are identical and store it in `is_same::value`
/// \tparam T0 first type to check
@@ -664,7 +785,7 @@ template<typename T0, typename T1> struct is_same : false_type {};
template<typename T> struct is_same<T, T> : true_type {};
///
/// \brief shorthand for ```is_same<T0, T1>::value```
/// \brief Shorthand for ```is_same<T0, T1>::value```
/// \tparam T0 first type to check
/// \tparam T1 second type to check
template<typename T0, typename T1> constexpr bool_t is_same_v = is_same<T0, T1> {};
@@ -672,63 +793,63 @@ template<typename T0, typename T1> constexpr bool_t is_same_v = is_same<T0, T1>
// fennec::is_complete ==============================================================================================
///
/// \brief check if type `T` is complete
/// \brief Check if type `T` is complete
///
/// \details Checks if `T`
/// \tparam T type to check
template<typename T> struct is_complete : detail::_is_complete<T>::type {};
///
/// \brief shorthand for `is_complete<TypeT0, TypeT1>::value`
/// \brief Shorthand for `is_complete<TypeT0, TypeT1>::value`
/// \tparam T type to check
template<typename T> constexpr bool_t is_complete_v = is_complete<T>{};
// fennec::is_iterable ==============================================================================================
///
/// \brief check if type `T` is iterable
/// \brief Check if type `T` is iterable
///
/// \details Checks if `T`
/// \tparam T type to check
template<typename T> struct is_iterable : decltype(detail::_is_iterable<T>(0)) {};
///
/// \brief shorthand for `is_iterable<TypeT0, TypeT1>::value`
/// \brief Shorthand for `is_iterable<TypeT0, TypeT1>::value`
/// \tparam T type to check
template<typename T> constexpr bool_t is_iterable_v = is_iterable<T>{};
// fennec::is_indexable ==============================================================================================
///
/// \brief check if type `T` is indexable
/// \brief Check if type `T` is indexable
///
/// \details Checks if `T`
/// \tparam T type to check
template<typename T> struct is_indexable : decltype(detail::_is_indexable<T>(0)) {};
///
/// \brief shorthand for `is_indexable<TypeT0, TypeT1>::value`
/// \brief Shorthand for `is_indexable<TypeT0, TypeT1>::value`
/// \tparam T type to check
template<typename T> constexpr bool_t is_indexable_v = is_indexable<T>{};
// fennec::is_mappable ==============================================================================================
///
/// \brief check if type `T` is mappable
/// \brief Check if type `T` is mappable
///
/// \details Checks if `T`
/// \tparam T type to check
template<typename T> struct is_mappable : decltype(detail::_is_mappable<T>(0)) {};
///
/// \brief shorthand for `is_mappable<TypeT0, TypeT1>::value`
/// \brief Shorthand for `is_mappable<TypeT0, TypeT1>::value`
/// \tparam T type to check
template<typename T> constexpr bool_t is_mappable_v = is_mappable<T>{};
// fennec::is_convertible ==============================================================================================
///
/// \brief check if type `T0` can be converted `T1`
/// \brief Check if type `T0` can be converted `T1`
///
/// \details Checks if `TypeT0`
/// \tparam FromT First type
@@ -737,7 +858,7 @@ template<typename FromT, typename ToT> struct is_convertible
: bool_constant<FENNEC_BUILTIN_IS_CONVERTIBLE(FromT, ToT)> {};
///
/// \brief shorthand for `can_convert<TypeT0, TypeT1>::value`
/// \brief Shorthand for `can_convert<TypeT0, TypeT1>::value`
/// \param FromT First type
/// \param ToT Second type
template<typename FromT, typename ToT> constexpr bool_t is_convertible_v = is_convertible<FromT, ToT>{};
@@ -779,7 +900,7 @@ template<typename ClassT> struct is_default_constructible
///
/// \brief Shorthand for `is_default_constructible<ClassT>::value`
template<typename ClassT, typename...ArgsT> constexpr bool_t is_default_constructible_v = is_default_constructible<ClassT>{};
template<typename ClassT> constexpr bool_t is_default_constructible_v = is_default_constructible<ClassT>{};

View File

@@ -213,6 +213,8 @@ namespace fennec
using bool_t = bool; ///< \brief A conditional type
using byte_t = unsigned char; ///< \brief A type capable of holding a single byte
using char_t = char; ///< \brief A type capable of holding an ascii value
using schar_t = signed char; ///< \brief A type with the size of a char, capable of holding a signed 8-bit integer
using uchar_t = unsigned char; ///< \brief A type with the size of a char, capable of holding an unsigned 8-bit integer

View File

@@ -26,6 +26,7 @@
#include <fennec/platform/interface/fwd.h>
#include <fennec/platform/interface/window.h>
#include <fennec/rtti/enable.h>
#include <fennec/rtti/singleton.h>
#include <fennec/rtti/detail/_this_t.h>
/*
@@ -62,12 +63,12 @@
namespace fennec
{
class platform {
class platform : singleton<platform*> {
public:
using shared_object = struct shared_object;
using symbol = void*;
platform() = default;
platform();
virtual ~platform() = default;
platform(const platform&) = delete;

View File

@@ -32,6 +32,9 @@ public:
void initialize() override;
void shutdown() override;
FENNEC_RTTI_CLASS_ENABLE(unix_platform) {
}
};
}

View File

@@ -35,7 +35,9 @@ public:
symbol find_symbol(shared_object* obj, const cstring& name) override;
private:
FENNEC_RTTI_CLASS_ENABLE(platform) {
}
};
}

View File

@@ -41,7 +41,7 @@
#define FENNEC_RTTI_CLASS_ENABLE(...) \
public: \
using super_class_list = fennec::typelist<__VA_ARGS__>; \
virtual fennec::type get_type() { return fennec::type::get_from_instance(this); } \
virtual inline fennec::type get_type() const { return fennec::type::get_from_instance(this); } \
FENNEC_DEFINE_THIS_T; \
private: \
FENNEC_CLASS_STATIC_CONSTRUCTOR(_init_reflection)

View File

@@ -31,4 +31,17 @@
#ifndef FENNEC_RTTI_FORWARD_H
#define FENNEC_RTTI_FORWARD_H
namespace fennec
{
class function;
struct vtypelist;
template<typename...> struct typelist;
struct type;
struct type_data;
struct type_storage;
}
#endif // FENNEC_RTTI_FORWARD_H

View File

@@ -0,0 +1,52 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 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 singleton.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_RTTI_SINGLETON_H
#define FENNEC_RTTI_SINGLETON_H
namespace fennec
{
template<typename T>
struct singleton {
static T& instance() requires(is_pointer_v<T>) {
static T instance = nullptr;
return instance;
}
static T& instance() requires(is_default_constructible_v<T> and not is_pointer_v<T>) {
static T instance;
return instance;
}
};
}
#endif // FENNEC_RTTI_SINGLETON_H

View File

@@ -36,7 +36,7 @@
#include <fennec/string/string.h>
#include <fennec/memory/pointers.h>
#include <fennec/rtti/typeid.h>
#include <fennec/rtti/typelist.h>
#include <fennec/rtti/forward.h>
#include <fennec/rtti/detail/_type_name.h>
namespace fennec

View File

@@ -0,0 +1,85 @@
// =====================================================================================================================
// fennec, a free and open source game engine
// Copyright © 2025 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 type_registry.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_RTTI_TYPE_REGISTRY_H
#define FENNEC_RTTI_TYPE_REGISTRY_H
#include <fennec/containers/priority_queue.h>
#include <fennec/rtti/type.h>
namespace fennec
{
template<typename BaseT, typename...ArgsT>
class type_registry {
public:
using ctor_t = BaseT (*)(ArgsT&&...);
struct entry {
size_t priority;
type type;
ctor_t ctor;
};
struct compare {
bool operator()(const entry& a, const entry& b) const {
return a.priority > b.priority and a.ctor < b.ctor;
}
};
using entrylist_t = priority_queue<entry, compare>;
template<typename T>
static void register_type(size_t priority = 0) {
_global_list().emplace(
priority,
type::get<T>(),
_constructor_helper<ArgsT...>
);
}
static const entrylist_t& get_type_list() {
return _global_list();
}
private:
static entrylist_t& _global_list() {
static entrylist_t list;
return list;
}
template<typename T>
static BaseT* _constructor_helper(ArgsT&&...args) {
return new T(fennec::forward<ArgsT>(args)...);
}
};
}
#endif // FENNEC_RTTI_TYPE_REGISTRY_H

View File

@@ -32,13 +32,23 @@
#define FENNEC_RTTI_TYPELIST_H
#include <fennec/lang/types.h>
#include <fennec/rtti/type.h>
namespace fennec
{
struct vtypelist {
virtual ~vtypelist() = default;
virtual dynarray<type> get() = 0;
};
template<typename...TypesT>
struct typelist {
struct typelist : vtypelist {
static constexpr size_t size = sizeof...(TypesT);
virtual dynarray<type> get() {
return { type::get<TypesT>()... };
}
};
}

View File

@@ -40,7 +40,7 @@ Library and Template Library.
| tuple | 🚧 | 🚧 |
| optional | ✅ | ✅ |
| variant | ⛔ | ⛔ |
| any | ⛔ | ⛔ |
| generic (`std::any`) | ⛔ | ⛔ |
| bitset | ⛔ | ⛔ |
| array | ✅ | ✅ |
| dynarray (`std::vector`) | 🚧 | 🚧 |

View File

@@ -21,6 +21,11 @@
namespace fennec
{
platform::platform() {
assertf(instance() == nullptr, "Attempted to instantiate multiple platforms.");
instance() = this;
}
void platform::initialize() {
}