- RTTI properties for types for iterators, indexing, and mapping
This commit is contained in:
@@ -265,6 +265,7 @@ add_library(fennec STATIC
|
||||
include/fennec/langcpp/ranges.h
|
||||
include/fennec/langcpp/declval.h
|
||||
include/fennec/langcpp/detail/_declval.h
|
||||
include/fennec/containers/bitfield.h
|
||||
)
|
||||
|
||||
add_dependencies(fennec metaprogramming fennec-dependencies)
|
||||
|
||||
108
include/fennec/containers/bitfield.h
Normal file
108
include/fennec/containers/bitfield.h
Normal 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
|
||||
@@ -102,6 +102,27 @@ namespace fennec::detail
|
||||
template<typename T>
|
||||
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
|
||||
|
||||
@@ -697,6 +697,34 @@ template<typename T> struct is_iterable : decltype(detail::_is_iterable<T>(0)) {
|
||||
/// \tparam T type to check
|
||||
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 ==============================================================================================
|
||||
|
||||
///
|
||||
|
||||
@@ -42,29 +42,56 @@ struct type {
|
||||
///
|
||||
/// \returns A const-qualified reference to a string containing the name of the type
|
||||
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
|
||||
uint64_t id() const {
|
||||
return _data->uuid;
|
||||
return _data ? _data->uuid : 0;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A dynarray of all the super (base) types of the type
|
||||
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
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
||||
///
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#ifndef FENNEC_RTTI_TYPE_DATA_H
|
||||
#define FENNEC_RTTI_TYPE_DATA_H
|
||||
|
||||
#include <fennec/containers/bitfield.h>
|
||||
#include <fennec/containers/dynarray.h>
|
||||
#include <fennec/containers/optional.h>
|
||||
#include <fennec/lang/strings/string.h>
|
||||
@@ -42,7 +43,15 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
enum type_prop_ {
|
||||
type_prop_complete = 0,
|
||||
type_prop_iterable,
|
||||
type_prop_indexable,
|
||||
type_prop_mappable,
|
||||
};
|
||||
|
||||
struct type_data {
|
||||
|
||||
uint64_t uuid;
|
||||
string name;
|
||||
|
||||
@@ -50,12 +59,16 @@ struct type_data {
|
||||
dynarray<type_data*> supers;
|
||||
dynarray<type_data*> subs;
|
||||
|
||||
bool is_iterable;
|
||||
// TODO: Change to bitfield
|
||||
bitfield<8> properties;
|
||||
|
||||
type_data* key_type;
|
||||
};
|
||||
|
||||
struct type_storage {
|
||||
private:
|
||||
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;
|
||||
@@ -75,7 +88,9 @@ private:
|
||||
.supers = type_storage::get_data(supers_t{}),
|
||||
.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) {
|
||||
@@ -87,7 +102,7 @@ private:
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
static type_data* get_data() {
|
||||
static type_data* get_data() requires(not is_void_v<T>) {
|
||||
auto& typelist = _typelist();
|
||||
uint64_t uuid = typeuuid<T>();
|
||||
|
||||
@@ -102,6 +117,11 @@ public:
|
||||
return typelist[uuid].get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static type_data* get_data() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename...Ts>
|
||||
static dynarray<type_data*> get_data(typelist<Ts...>) {
|
||||
return {
|
||||
|
||||
@@ -56,11 +56,33 @@ inline void fennec_test_rtti() {
|
||||
fennec_test_run(string(detail::type_name<int>()), string("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>().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_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>());
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user