- Outlined more functions for component systems
- Tidied up map and set structures to invoke less constructors and assignments
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -51,5 +51,5 @@ Library and Template Library.
|
||||
|
||||
| Symbol | Implemented | Passed |
|
||||
|:--------|:-----------:|:------:|
|
||||
| graph | ✔ | ✔ |
|
||||
| graph | ❌ | ❌ |
|
||||
| rd_tree | ✔ | ✔ |
|
||||
|
||||
Reference in New Issue
Block a user