- RTTI properties for types for iterators, indexing, and mapping

This commit is contained in:
2025-11-30 20:58:56 -05:00
parent 6f09c3f7fe
commit 0b76b06a1b
7 changed files with 236 additions and 9 deletions

View File

@@ -265,6 +265,7 @@ add_library(fennec STATIC
include/fennec/langcpp/ranges.h include/fennec/langcpp/ranges.h
include/fennec/langcpp/declval.h include/fennec/langcpp/declval.h
include/fennec/langcpp/detail/_declval.h include/fennec/langcpp/detail/_declval.h
include/fennec/containers/bitfield.h
) )
add_dependencies(fennec metaprogramming fennec-dependencies) add_dependencies(fennec metaprogramming fennec-dependencies)

View File

@@ -0,0 +1,108 @@
// =====================================================================================================================
// 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 bitfield.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_BITFIELD_H
#define FENNEC_CONTAINERS_BITFIELD_H
#include <fennec/containers/array.h>
#include <fennec/langcpp/types.h>
namespace fennec
{
///
/// \brief Bitfield Container with basic Bit Ops
/// \tparam N The number of bits in the bitfield
template<size_t N>
struct bitfield {
static constexpr size_t size = N;
public:
constexpr bitfield()
: _bytes() {
}
constexpr bitfield(const bool (&arr)[N]) {
for (size_t i = 0; i < arr; ++i) {
this->store(i, arr[i]);
}
}
template<typename...ArgsT>
constexpr bitfield(ArgsT&&...args) {
size_t i = 0;
(this->store(i++, fennec::forward<ArgsT>(args)), ...);
}
constexpr ~bitfield() = default;
bool test(size_t i) const {
assertd(i < size, "Index out of Bounds!");
size_t b = i / 8;
size_t o = i % 8;
return _bytes[b] & (1 << o);
}
void set(size_t i) {
assertd(i < size, "Index out of Bounds!");
size_t b = i / 8;
size_t o = i % 8;
_bytes[b] |= (1 << o);
}
void clear(size_t i) {
assertd(i < size, "Index out of Bounds!");
size_t b = i / 8;
size_t o = i % 8;
_bytes[b] &= ~(1 << o);
}
void toggle(size_t i) {
assertd(i < size, "Index out of Bounds!");
size_t b = i / 8;
size_t o = i % 8;
_bytes[b] ^= (1 << o);
}
void store(size_t i, bool v) {
assertd(i < size, "Index out of Bounds!");
size_t b = i / 8;
size_t o = i % 8;
(_bytes[b] &= ~((1 << o))) |= ((v << o));
}
private:
array<uint8_t, (N + 7) / 8> _bytes;
};
}
#endif // FENNEC_CONTAINERS_BITFIELD_H

View File

@@ -102,6 +102,27 @@ namespace fennec::detail
template<typename T> template<typename T>
auto _is_iterable(...) -> false_type; auto _is_iterable(...) -> false_type;
template<typename T>
auto _is_indexable(int) -> decltype(
declval<T&>()[0],
true_type{}
);
template<typename T>
auto _is_indexable(...) -> false_type;
template<typename T>
auto _is_mappable(int) -> decltype(
declval<T&>()[declval<typename T::key_t&>()],
true_type{}
);
template<typename T>
auto _is_mappable(...) -> false_type;
} }
#endif // FENNEC_LANG_DETAIL_TYPE_TRAITS_H #endif // FENNEC_LANG_DETAIL_TYPE_TRAITS_H

View File

@@ -697,6 +697,34 @@ template<typename T> struct is_iterable : decltype(detail::_is_iterable<T>(0)) {
/// \tparam T type to check /// \tparam T type to check
template<typename T> constexpr bool_t is_iterable_v = is_iterable<T>{}; template<typename T> constexpr bool_t is_iterable_v = is_iterable<T>{};
// fennec::is_complete ==============================================================================================
///
/// \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`
/// \tparam T type to check
template<typename T> constexpr bool_t is_indexable_v = is_indexable<T>{};
// fennec::is_complete ==============================================================================================
///
/// \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`
/// \tparam T type to check
template<typename T> constexpr bool_t is_mappable_v = is_mappable<T>{};
// fennec::is_convertible ============================================================================================== // fennec::is_convertible ==============================================================================================
/// ///

View File

@@ -42,29 +42,56 @@ struct type {
/// ///
/// \returns A const-qualified reference to a string containing the name of the type /// \returns A const-qualified reference to a string containing the name of the type
const string& name() const { const string& name() const {
return _data->name; static const string nullname = string("[none]");
return _data ? _data->name : nullname;
} }
/// ///
/// \returns An integer value containing the unique universal identifier for the type /// \returns An integer value containing the unique universal identifier for the type
uint64_t id() const { uint64_t id() const {
return _data->uuid; return _data ? _data->uuid : 0;
} }
/// ///
/// \returns A dynarray of all the super (base) types of the type /// \returns A dynarray of all the super (base) types of the type
dynarray<type> supertypes() const { dynarray<type> supertypes() const {
return _data->supers; return _data ? _data->supers : dynarray<type_data*>{};
} }
/// ///
/// \returns A dynarray of all the sub (child) types of the type /// \returns A dynarray of all the sub (child) types of the type
dynarray<type> subtypes() const { dynarray<type> subtypes() const {
return _data->subs; return _data ? _data->subs : dynarray<type_data*>{};
} }
///
/// \returns `true` if this is a complete type, false otherwise
bool is_complete() const {
return _data ? _data->properties.test(type_prop_complete) : false;
}
///
/// \returns `true` if this type fulfills the [C++11 range-initializer](https://en.cppreference.com/w/cpp/language/range-for.html), false otherwise
bool is_iterable() const { bool is_iterable() const {
return _data->is_iterable; return _data ? _data->properties.test(type_prop_iterable) : false;
}
///
/// \returns `true` if this type implements `operator[]` with a single parameter of integral type, false otherwise
bool is_indexable() const {
return _data ? _data->properties.test(type_prop_indexable) : false;
}
///
/// \returns `true` if this type implements `operator[]` with a single parameter of type `type::key_t`
bool is_mappable() const {
return _data ? _data->properties.test(type_prop_mappable) : false;
}
///
/// \returns The type used as a key for mappable types.
type key_type() const {
return _data ? _data->key_type : nullptr;
} }
/// ///

View File

@@ -31,6 +31,7 @@
#ifndef FENNEC_RTTI_TYPE_DATA_H #ifndef FENNEC_RTTI_TYPE_DATA_H
#define FENNEC_RTTI_TYPE_DATA_H #define FENNEC_RTTI_TYPE_DATA_H
#include <fennec/containers/bitfield.h>
#include <fennec/containers/dynarray.h> #include <fennec/containers/dynarray.h>
#include <fennec/containers/optional.h> #include <fennec/containers/optional.h>
#include <fennec/lang/strings/string.h> #include <fennec/lang/strings/string.h>
@@ -42,7 +43,15 @@
namespace fennec namespace fennec
{ {
enum type_prop_ {
type_prop_complete = 0,
type_prop_iterable,
type_prop_indexable,
type_prop_mappable,
};
struct type_data { struct type_data {
uint64_t uuid; uint64_t uuid;
string name; string name;
@@ -50,12 +59,16 @@ struct type_data {
dynarray<type_data*> supers; dynarray<type_data*> supers;
dynarray<type_data*> subs; dynarray<type_data*> subs;
bool is_iterable; // TODO: Change to bitfield
bitfield<8> properties;
type_data* key_type;
}; };
struct type_storage { struct type_storage {
private: private:
template<typename ClassT> using _super_class_list = typename ClassT::super_class_list; template<typename ClassT> using _super_class_list = typename ClassT::super_class_list;
template<typename ClassT> using _key_t = typename ClassT::key_t;
static dynarray<unique_ptr<type_data>>& _typelist() { static dynarray<unique_ptr<type_data>>& _typelist() {
static dynarray<unique_ptr<type_data>> typelist; static dynarray<unique_ptr<type_data>> typelist;
@@ -75,7 +88,9 @@ private:
.supers = type_storage::get_data(supers_t{}), .supers = type_storage::get_data(supers_t{}),
.subs = {}, .subs = {},
.is_iterable = is_iterable_v<T> .properties = { is_complete_v<T>, is_iterable_v<T>, is_indexable_v<T>, is_mappable_v<T> },
.key_type = type_storage::get_data<detect_t<void, _key_t, T>>(),
}); });
for (type_data* t : res->supers) { for (type_data* t : res->supers) {
@@ -87,7 +102,7 @@ private:
public: public:
template<typename T> template<typename T>
static type_data* get_data() { static type_data* get_data() requires(not is_void_v<T>) {
auto& typelist = _typelist(); auto& typelist = _typelist();
uint64_t uuid = typeuuid<T>(); uint64_t uuid = typeuuid<T>();
@@ -102,6 +117,11 @@ public:
return typelist[uuid].get(); return typelist[uuid].get();
} }
template<typename T>
static type_data* get_data() {
return nullptr;
}
template<typename...Ts> template<typename...Ts>
static dynarray<type_data*> get_data(typelist<Ts...>) { static dynarray<type_data*> get_data(typelist<Ts...>) {
return { return {

View File

@@ -56,11 +56,33 @@ inline void fennec_test_rtti() {
fennec_test_run(string(detail::type_name<int>()), string("int")); fennec_test_run(string(detail::type_name<int>()), string("int"));
fennec_test_run(type::get<int>(), type::get<signed int>()); fennec_test_run(type::get<int>(), type::get<signed int>());
fennec_test_spacer(1);
fennec_test_run(type::get<rtti_test_sub>().name(), string(detail::type_name<rtti_test_sub>())); fennec_test_run(type::get<rtti_test_sub>().name(), string(detail::type_name<rtti_test_sub>()));
fennec_test_run(type::get<rtti_test_sub>().supertypes()[0].name(), string(detail::type_name<rtti_test_base>())); fennec_test_run(type::get<rtti_test_sub>().supertypes()[0].name(), string(detail::type_name<rtti_test_base>()));
fennec_test_run(type::get<rtti_test_base>().subtypes()[0].name(), string(detail::type_name<rtti_test_sub>())); fennec_test_run(type::get<rtti_test_base>().subtypes()[0].name(), string(detail::type_name<rtti_test_sub>()));
fennec_test_run(type::get<dynarray<int>>().is_iterable(), true); fennec_test_spacer(1);
fennec_test_run(type::get<int>().is_complete(), true);
fennec_test_run(type::get<int>().is_iterable(), false);
fennec_test_run(type::get<int>().is_indexable(), false);
fennec_test_run(type::get<int>().is_mappable(), false);
fennec_test_spacer(1);
fennec_test_run(type::get<dynarray<int>>().is_complete(), true);
fennec_test_run(type::get<dynarray<int>>().is_iterable(), true);
fennec_test_run(type::get<dynarray<int>>().is_indexable(), true);
fennec_test_run(type::get<dynarray<int>>().is_mappable(), false);
fennec_test_spacer(1);
fennec_test_run((type::get<map<string, string>>().is_complete()), true);
fennec_test_run((type::get<map<string, string>>().is_iterable()), true);
fennec_test_run((type::get<map<string, string>>().is_indexable()), true);
fennec_test_run((type::get<map<string, string>>().is_mappable()), true);
fennec_test_run((type::get<map<string, string>>().key_type()), type::get<string>());
} }