- Outlined more functions for component systems

- Tidied up map and set structures to invoke less constructors and assignments
This commit is contained in:
2025-08-12 13:51:59 -04:00
parent d6e31a89b0
commit cc4d85c393
5 changed files with 247 additions and 128 deletions

View File

@@ -54,11 +54,14 @@ struct map {
// Definitions =========================================================================================================
public:
struct key_hash;
struct node_equals;
using key_t = KeyT;
using value_t = ValueT;
using elem_t = pair<KeyT, ValueT>;
using alloc_t = typename allocator_traits<Alloc>::template rebind<elem_t>;
using hash_t = Hash;
using set_t = set<elem_t, key_hash, node_equals, alloc_t>;
// We only want to hash the key
struct key_hash : hash_t {
@@ -93,19 +96,8 @@ public:
/// \param key Key value to access
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
constexpr value_t* operator[](const KeyT& key) {
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
pair<KeyT, char[sizeof(ValueT)]> root;
pair<KeyT, ValueT> val;
~U() {
fennec::destruct(&root);
}
} trick = { .root = { key, 0 } };
auto it = _set.find(trick.val);
if (it == _set.end()) {
return nullptr;
}
return &_set.at(it)->second;
auto it = _set.at(this->_find(key));
return it ? &it->second : nullptr;
}
///
@@ -113,19 +105,8 @@ public:
/// \param key Key value to access
/// \returns A const-qualified pointer to the value associated with `key`, `nullptr` if `key` is not present.
constexpr const value_t* operator[](const KeyT& key) const {
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
pair<KeyT, char[sizeof(ValueT)]> root;
pair<KeyT, ValueT> val;
~U() {
fennec::destruct(&root);
}
} trick = { .root = { key, 0 } }; // Only initialize root
auto it = _set.find(trick.val);
if (it == _set.end()) {
return nullptr;
}
return &_set.at(it)->second;
auto it = _set.at(this->_find(key));
return it ? &it->second : nullptr;
}
///
@@ -133,43 +114,21 @@ public:
/// \tparam ArgT Argument Type
/// \param arg Argument to construct the key with
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
template<typename ArgT>
constexpr value_t* operator[](ArgT&& arg) {
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
pair<KeyT, char[sizeof(ValueT)]> root;
pair<KeyT, ValueT> val;
~U() {
fennec::destruct(&root);
}
} trick = { .root = { key_t(fennec::forward<ArgT>(arg)), 0 } }; // Only initialize root
auto it = _set.find(trick.val);
if (it == _set.end()) {
return nullptr;
}
return &_set.at(it)->second;
template<typename...ArgsT>
constexpr value_t* operator[](ArgsT&&...args) {
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
return it ? &it->second : nullptr;
}
///
/// \brief Argument Key Const Access Operator
/// \tparam ArgT Argument Type
/// \param arg Argument to construct the key with
/// \tparam ArgsT Argument Type
/// \param args Argument to construct the key with
/// \returns A const-qualified pointer to the value associated with `key`, `nullptr` if `key` is not present.
template<typename ArgT>
constexpr const value_t* operator[](ArgT&& arg) const {
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
pair<KeyT, char[sizeof(ValueT)]> root;
pair<KeyT, ValueT> val;
~U() {
fennec::destruct(&root);
}
} trick = { .root = { key_t(fennec::forward<ArgT>(arg)), 0 } }; // Only initialize root
auto it = _set.find(trick.val);
if (it == _set.end()) {
return nullptr;
}
return &_set.at(it)->second;
template<typename...ArgsT>
constexpr const value_t* operator[](ArgsT&&...args) const {
auto it = _set.at(this->_find(fennec::forward<ArgsT>(args)...));
return it ? &it->second : nullptr;
}
@@ -179,12 +138,15 @@ public:
/// \brief Key-Value Insertion
/// \param pair a pair containing the key and its value
constexpr void insert(elem_t&& pair) {
auto it = _set.find(pair);
if (it == _set.end()) {
_set.at(it)->second = fennec::move(pair.second);
return;
}
_set.insert(fennec::forward<elem_t>(pair));
this->_insert(fennec::forward<elem_t>(pair));
}
///
/// \brief Key-Value Insertion
/// \param args Arguments for constructing the key-value pair
template<typename...ArgsT>
constexpr void emplace(const KeyT& key, ArgsT&&...args) {
this->_insert(key, fennec::forward<ArgsT>(args)...);
}
///
@@ -192,30 +154,21 @@ public:
/// \param args Arguments for constructing the key-value pair
template<typename...ArgsT>
constexpr void emplace(ArgsT&&...args) {
_set.insert(elem_t(args...));
this->_insert(fennec::forward<ArgsT>(args)...);
}
///
/// \brief Erase a key
/// \param key key to erase
constexpr void erase(KeyT&& key) {
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
pair<KeyT, char[sizeof(ValueT)]> root;
pair<KeyT, ValueT> val;
~U() {
fennec::destruct(&root);
}
} trick = { .root = { fennec::forward<KeyT>(key), 0 } };
_set.erase(trick.val);
_set.erase(this->_find(fennec::forward<KeyT>(key)));
}
///
/// \brief Erase a key
/// \param key key to erase
constexpr void erase(const KeyT& key) {
KeyT val = key;
erase(fennec::move(val));
_set.erase(this->_find(key));
}
///
@@ -224,6 +177,15 @@ public:
/// \param args Arguments to construct a key to erase
template<typename...ArgsT>
constexpr void erase(ArgsT&&...args) {
_set.erase(this->_find(fennec::forward<ArgsT>(args)...));
}
private:
set_t _set;
template<typename...ArgsT>
set_t::iterator _find(ArgsT&&...args) {
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
pair<KeyT, char[sizeof(ValueT)]> root;
pair<KeyT, ValueT> val;
@@ -232,12 +194,19 @@ public:
fennec::destruct(&root);
}
} trick = { .root = { KeyT(fennec::forward<ArgsT>(args)...), 0 } };
_set.erase(trick.val);
return _set.find(trick.val);
}
private:
set<elem_t, key_hash, node_equals, alloc_t> _set;
template<typename...ArgsT>
constexpr void _insert(ArgsT&&...args) {
elem_t elem(fennec::forward<ArgsT>(args)...);
auto it = this->_find(elem.first);
if (it != _set.end()) {
_set.at(it)->second = fennec::move(elem.second);
} else {
_set.insert(fennec::move(elem));
}
}
};
}

View File

@@ -215,6 +215,9 @@ public:
/// \param val Value to find
/// \returns An iterator at the location of the value
constexpr iterator find(const elem_t& val) const {
if (capacity() == 0) {
return end();
}
size_t s = _hash(val) % capacity(); // Initial search index
int psl = (_size != 0) ? _sumpsl / _size : 0; // Initial psl
size_t i = (s + psl) % capacity(); // Median search
@@ -233,15 +236,15 @@ public:
size_t i0 = (i + capacity() - n) % capacity(); // Prevent index underflow
size_t i1 = (i + n) % capacity();
int p0 = psl - n, p1 = psl + n;
bool c0 = false, c1 = false;
bool c0 = p0 >= 0 && _alloc[i0].psl >= p0, c1 = _alloc[i1].psl >= p1; // Check that we are in range
if ((c0 = (p0 >= 0 && _alloc[i0].psl >= p0)) && _alloc[i0].value) {
if (c0 && _alloc[i0].value) {
if (*_alloc[i0].value == val) {
return iterator(this, i);
}
}
if ((c1 = (_alloc[i1].psl >= p1)) && _alloc[i1].value) {
if (c1 && _alloc[i1].value) {
if (*_alloc[i1].value == val) {
return iterator(this, i);
}
@@ -269,7 +272,12 @@ public:
/// \returns A pointer to the element, `nullptr` if not found.
/// The value should not be changed in a manner that will change the hash of the element.
constexpr elem_t* at(const iterator& it) {
if (not _alloc[it._i].value) return nullptr;
if (it == end()) {
return nullptr;
}
if (not _alloc[it._i].value) {
return nullptr;
}
return &*_alloc[it._i].value;
}
@@ -288,35 +296,14 @@ public:
/// \brief Move Insertion
/// \param val Value to insert
constexpr void insert(elem_t&& val) {
if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full
_expand();
}
elem_t value = fennec::forward<elem_t>(val);
size_t i = _hash(value) % capacity(); // Initial search index
int psl = 0;
while (_alloc[i].value) { // Search for empty cell
if (_equal(*_alloc[i].value, val)) { // Check to see if this element is already inserted
return;
}
if (psl > _alloc[i].psl) { // When psl is higher, swap
_sumpsl += psl - _alloc[i].psl;
fennec::swap(_alloc[i].psl, psl);
fennec::swap(*_alloc[i].value, value);
}
i = (i + 1) % capacity(); ++psl;
}
_alloc[i].value = fennec::move(value);
_sumpsl += (_alloc[i].psl = psl);
++_size;
this->_insert(fennec::forward<elem_t>(val));
}
///
/// \brief Copy Insertion
/// \param val Value to insert
constexpr void insert(const elem_t& val) {
elem_t value = val; // Copy Constructor invoked here
this->insert(fennec::move(value)); // Only invokes moves
this->_insert(val);
}
///
@@ -325,8 +312,7 @@ public:
/// \param args Arguments to construct with
template<typename...ArgsT>
constexpr void emplace(ArgsT&&...args) {
elem_t value = elem_t(fennec::forward<ArgsT>(args)...); // Constructor invoked here
this->insert(fennec::move(value)); // Only invokes moves
this->_insert(fennec::forward<ArgsT>(args)...);
}
///
@@ -398,11 +384,11 @@ public:
return &*_set->_alloc[_i].value;
}
constexpr bool operator==(const iterator& it) {
constexpr bool operator==(const iterator& it) const {
return _set == it._set and _i == it._i;
}
constexpr bool operator!=(const iterator& it) {
constexpr bool operator!=(const iterator& it) const {
return _set != it._set or _i != it._i;
}
@@ -450,6 +436,31 @@ private:
fennec::swap(_alloc, cpy._alloc);
}
template<typename...ArgsT>
constexpr void _insert(ArgsT&&...args) {
if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full
_expand();
}
elem_t value(fennec::forward<ArgsT>(args)...);
size_t i = _hash(value) % capacity(); // Initial search index
int psl = 0;
while (_alloc[i].value) { // Search for empty cell
if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted
return;
}
if (psl > _alloc[i].psl) { // When psl is higher, swap
_sumpsl += psl - _alloc[i].psl;
fennec::swap(_alloc[i].psl, psl);
fennec::swap(*_alloc[i].value, value);
}
i = (i + 1) % capacity(); ++psl;
}
_alloc[i].value = fennec::move(value);
_sumpsl += (_alloc[i].psl = psl);
++_size;
}
allocation<node, alloc_t> _alloc;
hash_t _hash;
equal_t _equal;

View File

@@ -20,6 +20,8 @@
#define FENNEC_SCENE_COMPONENT_H
#include <fennec/containers/dynarray.h>
#include <fennec/containers/map.h>
#include <fennec/containers/optional.h>
#include <fennec/langproc/strings/string.h>
#include <fennec/lang/typeuuid.h>
@@ -28,40 +30,176 @@ namespace fennec
class component {
public:
using component_create = component (*)(size_t);
using component_find = component* (*)(size_t);
using component_erase = void (*)(size_t);
struct type {
string name;
component_create create;
component_find find;
component_erase erase;
// TYPEDEFS & CONSTANTS ================================================================================================
using component_create = component (*)(size_t);
using component_find = component* (*)(size_t);
using component_destroy = void (*)(size_t);
using component_tick = void (*)(size_t, double);
using component_frame = void (*)(size_t, uint64_t);
// TYPE OPERATIONS =====================================================================================================
struct type_info {
string name;
component_create create;
component_find find;
component_destroy destroy;
component_tick tick;
component_frame frame;
};
private:
static dynarray<type>& _type_list() {
static dynarray<type> type_list;
// Private Registry
static auto& _type_list() {
static dynarray<optional<type_info>> type_list;
return type_list;
}
static void _register_type(uint64_t id, const cstring& name, component_create create, component_find find, component_erase erase) {
dynarray<type>& type_list = _type_list();
static void _register_type( uint64_t id, const cstring& name,
component_create create, component_find find, component_destroy destroy,
component_tick tick, component_frame frame) {
auto& type_list = _type_list();
if (id > type_list.size()) {
type_list.resize(id + 1);
}
type_list[id] = { name, create, find, erase };
type_list[id] = type_info{ name, create, find, destroy, tick, frame };
}
static void _unregister_type(uint64_t id) {
_type_list()[id] = nullopt;
}
static auto& _default_storage() {
static map<uint64_t, map<size_t, component*>> data;
return data;
}
public:
// Public Registry
///
/// \brief Get an uuid for the specified component type
/// \tparam ComponentT The component type
/// \returns An uuid that is associated with the type
template<typename ComponentT>
static void register_type(const cstring& name, component_create create, component_find find, component_erase erase) {
component::_register_type(typeuuid<ComponentT>(), name, create, find, erase);
static uint64_t uuid() {
return typeuuid<ComponentT, component>();
}
static const dynarray<type>& type_list() {
///
/// \brief Default storage function for default creation, retrieval, and destruction
template<typename ComponentT>
static constexpr auto& default_storage() {
auto& data = _default_storage();
return data[uuid<ComponentT>()];
}
///
/// \brief Default creation function for a component
/// \tparam ComponentT Type of the component
/// \param node The node to associate the component with
/// \returns The component created by this function
template<typename ComponentT>
static constexpr component* default_create(size_t node) {
auto& storage = default_storage<ComponentT>();
if (not storage[node]) {
storage.emplace(node);
}
return storage[node];
}
///
/// \brief Default retrieval function for a component
/// \tparam ComponentT Type of the component
/// \param node The node the component is associated with
/// \returns The component found by this function
template<typename ComponentT>
static constexpr component* default_find(size_t node) {
auto& storage = default_storage<ComponentT>();
return storage[node];
}
///
/// \brief Default destruction function for a component
/// \tparam ComponentT Type of the component
/// \param node The node the component is associated with
template<typename ComponentT>
static constexpr void default_destroy(size_t node) {
default_storage<ComponentT>().erase(node);
}
///
/// \brief Register a type with the component system
/// \tparam ComponentT The component type
/// \param name The name of the type
/// \param create The function used to create a component, given a scene node. **MUST NOT BE NULL**
/// \param find The function used to find a component, given a scene node. **MUST NOT BE NULL**
/// \param destroy The function used to destroy a component of a scene node. **MUST NOT BE NULL**
template<typename ComponentT>
static void register_type(const cstring& name, component_tick tick, component_frame frame,
component_create create = default_create<ComponentT>,
component_find find = default_find<ComponentT>,
component_destroy destroy = default_destroy<ComponentT>) {
component::_register_type(uuid<ComponentT>(), name, create, find, destroy, tick, frame);
}
template<typename ComponentT>
static void unregister_type() {
component::_unregister_type(uuid<ComponentT>());
}
///
/// \brief Create a component of type `ComponentT`
/// \tparam ComponentT The type to create
/// \param node The node to associate the component with
/// \returns The created component
template<typename ComponentT>
static component* create(size_t node) {
auto& type = _type_list()[uuid<ComponentT>()];
return type ? *type->create(node) : nullptr;
}
template<typename ComponentT>
static component* find(size_t node) {
auto& type = _type_list()[uuid<ComponentT>()];
return type ? *type->find(node) : nullptr;
}
template<typename ComponentT>
static void destroy(size_t node) {
auto& type = _type_list()[uuid<ComponentT>()];
return type ? *type->destroy(node) : nullptr;
}
static void tick(size_t node, double dT) {
for (auto& it : _type_list()) {
it->tick(node, dT);
}
}
static void frame(size_t node, uint64_t f) {
for (auto& it : _type_list()) {
it->tick(node, f);
}
}
static const auto& type_list() {
return _type_list();
}
// MEMBERS =============================================================================================================
public:
const type_info* type;
const size_t node;
template<typename ComponentT>
component(size_t node)
: type(type_list()[uuid<ComponentT>()])
, node(node) {
}
};
}

View File

@@ -18,6 +18,7 @@
#ifndef FENNEC_CORE_SCENE_H
#define FENNEC_CORE_SCENE_H
#include <fennec/containers/rdtree.h>
#include <fennec/langproc/strings/string.h>