- Adjustments to component system design.

- Added indexed parameters to format strings
This commit is contained in:
2025-10-06 12:47:11 -04:00
parent 7b87828f06
commit b9026ec8da
12 changed files with 207 additions and 199 deletions

View File

@@ -247,6 +247,7 @@ add_library(fennec STATIC
# EXTRA SOURCES ======================================================================================================== # EXTRA SOURCES ========================================================================================================
${FENNEC_EXTRA_SOURCES} ${FENNEC_EXTRA_SOURCES}
include/fennec/gfx3d/mesh_instance.h
) )
add_dependencies(fennec metaprogramming fennec-dependencies) add_dependencies(fennec metaprogramming fennec-dependencies)

View File

@@ -18,7 +18,7 @@
# this script sets flags and variables for gnu and gnu-like compilers # this script sets flags and variables for gnu and gnu-like compilers
add_compile_options("-Wall" "-Wextra" "-pedantic" "-Werror") add_compile_options("-Wall" "-Wextra" "-pedantic" "-Werror" "-fms-extensions")
fennec_add_link_options("-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates") fennec_add_link_options("-nostdlib" "-fno-exceptions" "-fno-rtti" "-fdiagnostics-all-candidates")

View File

@@ -0,0 +1,47 @@
// =====================================================================================================================
// 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 mesh_instance.h
/// \brief
///
///
/// \details
/// \author Medusa Slockbower
///
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
///
///
#ifndef FENNEC_GFX3D_MESH_INSTANCE_H
#define FENNEC_GFX3D_MESH_INSTANCE_H
#include <fennec/scene/component.h>
namespace fennec
{
class mesh_instance : component {
public:
private:
};
}
#endif // FENNEC_GFX3D_MESH_INSTANCE_H

View File

@@ -20,7 +20,7 @@
#define FENNEC_LANG_STARTUP_H #define FENNEC_LANG_STARTUP_H
// Helper for running a function before main() // Helper for running a function before main()
#define STATIC_CONSTRUCTOR(f) \ #define FENNEC_STATIC_CONSTRUCTOR(f) \
inline static void f(void); \ inline static void f(void); \
struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \ struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \
inline static void f(void) inline static void f(void)

View File

@@ -54,6 +54,11 @@ public:
typed(TypeT*) typed(TypeT*)
: type(typeuuid<TypeT, RootT>()) { : type(typeuuid<TypeT, RootT>()) {
} }
template<typename TypeT>
static uint64_t id() {
return typeuuid<TypeT, RootT>();
}
}; };
} }

View File

@@ -66,12 +66,16 @@ struct format_string {
private: private:
static constexpr size_t npos = -1; static constexpr size_t npos = -1;
enum token_ : uint8_t { enum token_ : bool {
token_text = 0, token_text = false,
token_param, token_param = true,
}; };
using token = pair<uint8_t, string_view>; struct token {
uint8_t type;
string_view view;
size_t param;
};
constexpr const char* find(const char* start, char c) { constexpr const char* find(const char* start, char c) {
while (*start != c && *start != '\0') { while (*start != c && *start != '\0') {
@@ -80,47 +84,64 @@ private:
return start; return start;
} }
constexpr size_t parse_index(const char* start, size_t n) {
size_t x = 0;
while (n-- > 0) {
x *= 10;
x += *start - '0';
}
return x;
}
// Constructors & Destructor =========================================================================================== // Constructors & Destructor ===========================================================================================
public: public:
format_string() = delete; format_string() = delete;
format_string(const char (&str)[N]) constexpr format_string(const char (&str)[N])
: _tokens(), _num_tokens(0) { : _tokens(), _num_tokens(0) {
static_assert(is_constant_evaluated(), "Runtime format strings are not supported.");
// TODO: Numbered Parameters // TODO: Numbered Parameters
size_t params = 0; size_t params = 0;
for (size_t i = 0; i < N; ++i) { for (size_t i = 0; i < N - 1; ++i) {
size_t n = this->find(str + i, '{') - str; size_t l = this->find(str + i, '{') - str;
// Push the current token // Push the current token
if (n - i > 0) { if (l - i > 0) {
_tokens[_num_tokens++] = { token_text, { &str[i], n - i } }; _tokens[_num_tokens++] = { token_text, { &str[i], l - i }, npos };
} }
// no more braces, break // no more braces, break
if (n >= N) { if (l >= N) {
break; break;
} }
// escaped brace // escaped brace
if (str[n + 1] == '{') { if (str[l + 1] == '{') {
_tokens[_num_tokens++] = { token_text, { &str[n], 1 } }; _tokens[_num_tokens++] = { token_text, { &str[l], 1 }, npos };
i = n + 1; i = l + 1;
continue; continue;
} }
size_t e = this->find(str + n, '}') - str; size_t e = this->find(str + l, '}') - str;
//static_assert(e >= N, "Malformed format string, mismatched brace"); assertd(e < N, "Malformed format string, mismatched brace");
_tokens[_num_tokens++] = { token_param, { &str[n], e - n + 1 } }; // Colon found
++params; size_t c = this->find(str + l, ':') - str;
if (c < e) {
size_t n = c - l;
if (n > 1) { // Check if index is provided
params = this->parse_index(str + l + 1, n - 1);
}
l = c;
} else if (e - l > 1) { // check if index is provided
params = this->parse_index(str + l + 1, e - l - 1);
}
assertd(params < sizeof...(ArgsT), "Malformed format string, parameter count does not match argument count.");
_tokens[_num_tokens++] = { token_param, { &str[l + 1], e - l - 1 }, params++ };
i = e; i = e;
} }
//static_assert(params == sizeof...(ArgsT), "Malformed format string, parameter count does not match argument count.");
} }
constexpr format_string(const format_string&) = default; constexpr format_string(const format_string&) = default;
@@ -129,33 +150,48 @@ public:
~format_string() = default; ~format_string() = default;
string format(ArgsT&&...args) const { string format(ArgsT&&...args) const {
return this->_format(0, fennec::forward<ArgsT>(args)...); string parts[N];
// Copy raw text tokens
size_t i = 0;
for (i = 0; i < _num_tokens; ++i) {
const token& tok = _tokens[i];
if (not tok.type) {
parts[i] = tok.view;
}
}
// Copy arguments
this->_format(0, parts, fennec::forward<ArgsT>(args)...);
string res;
for (i = 0; i < _num_tokens; ++i) {
res += parts[i];
}
return res;
} }
private: private:
array<token, N> _tokens; array<token, N> _tokens;
size_t _num_tokens; size_t _num_tokens;
string _format(size_t) const { void _format(size_t) const {
return string{""};
} }
template<typename HeadT, typename...RestT> template<typename HeadT, typename...RestT>
string _format(size_t i, HeadT&& head, RestT&&...rest) const { void _format(size_t p, string (&parts)[N], HeadT&& head, RestT&&...rest) const {
string res; size_t i = 0;
for (; i < _num_tokens; ++i) { for (const token& tok : _tokens) {
const auto& token = _tokens[i]; if (i >= _num_tokens) {
if (token.first == token_param) {
formatter<HeadT> fmt;
res += fmt(token.second, head);
res += this->_format(i + 1, fennec::forward<RestT>(rest)...);
break; break;
} }
if (tok.param == p) {
res += token.second; formatter<HeadT> fmt;
parts[i] = fmt(tok.view, head);
}
++i;
} }
return res; this->_format(p + 1, fennec::forward<RestT>(rest)...);
} }
}; };

View File

@@ -55,9 +55,9 @@ private:
GLenum res; GLenum res;
// Set READ/DRAW/COPY // Set READ/DRAW/COPY
if constexpr (read) { if constexpr (map_read) {
res = GL_STREAM_READ; res = GL_STREAM_READ;
} else if constexpr (write) { } else if constexpr (map_write) {
res = GL_STREAM_DRAW; res = GL_STREAM_DRAW;
} else { } else {
res = GL_STREAM_COPY; res = GL_STREAM_COPY;

View File

@@ -21,30 +21,27 @@
#include <fennec/containers/dynarray.h> #include <fennec/containers/dynarray.h>
#include <fennec/containers/map.h> #include <fennec/containers/map.h>
#include <fennec/containers/object_pool.h>
#include <fennec/containers/optional.h> #include <fennec/containers/optional.h>
#include <fennec/containers/rdtree.h> #include <fennec/containers/rdtree.h>
#include <fennec/lang/startup.h>
#include <fennec/langproc/strings/string.h> #include <fennec/langproc/strings/string.h>
#include <fennec/lang/typed.h> #include <fennec/lang/typed.h>
#include <fennec/langproc/filesystem/path.h> #include <fennec/langproc/filesystem/path.h>
#define FENNEC_REGISTER_COMPONENT(T, p) \
FENNEC_STATIC_CONSTRUCTOR(T) { \
component::register_type<T>(p) \
}
namespace fennec namespace fennec
{ {
struct component_t {
uint64_t type;
size_t id;
};
class component : public typed<component> { class component : public typed<component> {
public: public:
// TYPEDEFS & CONSTANTS ================================================================================================ // TYPEDEFS & CONSTANTS ================================================================================================
using component_create = size_t (*)(size_t, size_t); using component_create = component* (*)(size_t, size_t);
using component_get = component* (*)(size_t); using component_destroy = void (*)(component*);
using component_destroy = void (*)(size_t);
using component_tick = void (*)(size_t, double);
using component_frame = void (*)(size_t, uint64_t);
// TYPE OPERATIONS ===================================================================================================== // TYPE OPERATIONS =====================================================================================================
@@ -52,31 +49,22 @@ public:
struct typeinfo { struct typeinfo {
string name; string name;
component_create create; component_create create;
component_destroy destroy;
component_get get;
component_tick tick;
component_frame frame;
typeinfo() typeinfo()
: name("") : name("")
, create(nullptr), destroy(nullptr) , create(nullptr) {
, get(nullptr)
, tick(nullptr), frame(nullptr) {
} }
typeinfo(const string& name, typeinfo(const string& name,
component_create create, component_destroy destroy, component_create create)
component_get get,
component_tick tick, component_frame frame)
: name(name) : name(name)
, create(create), destroy(destroy), get(get) , create(create) {
, tick(tick), frame(frame) {
} }
}; };
struct typeentry { struct typeentry {
string name; string name;
uint64_t id; uint64_t type;
}; };
using typelist_t = dynarray<optional<typeinfo>>; using typelist_t = dynarray<optional<typeinfo>>;
@@ -92,15 +80,13 @@ private:
static constexpr size_t npos = typetree_t::npos; static constexpr size_t npos = typetree_t::npos;
static constexpr size_t root = typetree_t::root; static constexpr size_t root = typetree_t::root;
static void _register_type( uint64_t id, const path& path, static void _register_type( uint64_t id, const path& path,
component_create create, component_destroy destroy, component_create create) {
component_get get,
component_tick tick, component_frame frame) {
// Register the type // Register the type
if (id > _typelist.size()) { if (id > _typelist.size()) {
_typelist.resize(id + 1); _typelist.resize(id + 1);
} }
_typelist[id] = typeinfo{ path.filename(), create, destroy, get, tick, frame }; _typelist[id] = typeinfo{ path.filename(), create };
// Create tree entry // Create tree entry
size_t node = root; size_t node = root;
@@ -112,7 +98,7 @@ private:
node = _typetree.child(parent); node = _typetree.child(parent);
while (node != npos && _typetree[node].id != id) { while (node != npos && _typetree[node].type != id) {
node = _typetree.next(node); node = _typetree.next(node);
} }
@@ -125,33 +111,6 @@ private:
} }
} }
struct noderef {
size_t scene, node;
};
using compstorage_t = object_pool<component*>; // Holds refs to default allocated components
using defaultstorage_t = map<uint64_t, compstorage_t>; // Maps types to respective storage
inline static defaultstorage_t _default_storage;
static auto& _type_info(uint64_t type) {
return _typelist[type];
}
template<typename ComponentT>
static auto& _type_info() {
return _typelist[uuid<ComponentT>()];
}
static auto& _type_storage(uint64_t type) {
return *_default_storage[type];
}
template<typename ComponentT>
static auto& _type_storage() {
return *_default_storage[uuid<ComponentT>()];
}
/// ///
/// \brief Default creation function for a component /// \brief Default creation function for a component
/// \tparam ComponentT Type of the component /// \tparam ComponentT Type of the component
@@ -159,60 +118,39 @@ private:
/// \returns The component created by this function /// \returns The component created by this function
template<typename ComponentT> template<typename ComponentT>
static constexpr component* default_create(size_t scene, size_t node) { static constexpr component* default_create(size_t scene, size_t node) {
auto& storage = _type_storage<ComponentT>(); return new ComponentT(scene, node);
return storage.insert(new ComponentT(scene, 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 comp) {
auto& storage = _type_storage<ComponentT>();
delete storage[comp];
storage.erase(comp);
}
///
/// \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_get(size_t comp) {
auto& storage = _type_storage<ComponentT>();
return storage[comp];
} }
// Public Registry ===================================================================================================== // Public Registry =====================================================================================================
public: public:
/// static constexpr const typetree_t& typelist() {
/// \brief Get an uuid for the specified component type return _typetree;
/// \tparam ComponentT The component type }
/// \returns An uuid that is associated with the type
template<typename ComponentT> template<typename ComponentT>
static uint64_t uuid() { static constexpr const typeinfo& type_info() {
return typeuuid<ComponentT, component>(); const auto& it = _typelist[id<ComponentT>()];
assert(it, "Unregistered type provided to component::type_info, see stacktrace for more info.");
return *it;
}
static constexpr const typeinfo& type_info(uint64_t type) {
const auto& it = _typelist[type];
assert(it, "Unregistered type provided to component::type_info, see stacktrace for more info.");
return *it;
} }
/// ///
/// \brief Register a type with the component system /// \brief Register a type with the component system
/// \tparam ComponentT The component type /// \tparam ComponentT The component type
/// \param name The name of the type /// \param path The name of the type
/// \param create The function used to create a component, given a scene node. **MUST NOT BE NULL** /// \param create The function used to create a component, given a scene node. **MUST NOT BE NULL**
/// \param get 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> template<typename ComponentT>
static void register_type(const cstring& name, static void register_type(const path& path,
component_tick tick, component_frame frame, component_create create = default_create<ComponentT>) {
component_create create = default_create<ComponentT>, component::_register_type(id<ComponentT>(), path, create);
component_get get = default_get<ComponentT>,
component_destroy destroy = default_destroy<ComponentT>) {
component::_register_type(uuid<ComponentT>(), name, create, destroy, get, tick, frame);
} }
/// ///
@@ -220,72 +158,25 @@ public:
/// \param type The type to create /// \param type The type to create
/// \param node The node to associate the component with /// \param node The node to associate the component with
/// \returns The created component /// \returns The created component
static size_t create(uint64_t type, size_t scene, size_t node) { static component* create(uint64_t type, size_t scene, size_t node) {
auto& typei = _typelist[type]; auto& typei = _typelist[type];
return typei->create(scene, node); return typei->create(scene, node);
} }
/// ///
/// \brief Create a component of type `ComponentT` /// \brief Create a component of type `type`
/// \tparam ComponentT The type to create /// \tparam ComponentT The type to create
/// \param node The node to associate the component with /// \param node The node to associate the component with
/// \returns The created component /// \returns The created component
template<typename ComponentT> template<typename ComponentT>
static size_t create(size_t scene, size_t node) { static component* create(size_t scene, size_t node) {
auto& typei = _typelist[uuid<ComponentT>()]; auto& typei = _typelist[id<ComponentT>()];
return typei->create(scene, node); return typei->create(scene, node);
} }
///
/// \brief Get a component of type `type` from id `id`
/// \param type The type of component
/// \param id The id of the component instance
/// \return
static component* get(const component_t& c) {
auto& typei = _typelist[c.type];
return typei->get(c.id);
}
template<typename ComponentT>
static component* get(size_t id) {
auto& typei = _typelist[uuid<ComponentT>()];
return typei->get(id);
}
static void destroy(size_t type, size_t comp) {
auto& typei = _typelist[type];
return typei->destroy(comp);
}
template<typename ComponentT>
static void destroy(size_t comp) {
auto& typei = _typelist[uuid<ComponentT>()];
return typei->destroy(comp);
}
static void tick(size_t comp, double dT) {
for (auto& it : _typelist) {
if (it->tick) {
it->tick(comp, dT);
}
}
}
static void frame(size_t comp, uint64_t f) {
for (auto& it : _typelist) {
if (it->frame) {
it->frame(comp, f);
}
}
}
static const auto& type_list() {
return _typelist;
}
// MEMBERS ============================================================================================================= // MEMBERS =============================================================================================================
public: public:
const size_t node; const size_t node;
template<typename ComponentT> template<typename ComponentT>
component(ComponentT* type, size_t node) component(ComponentT* type, size_t node)

View File

@@ -35,20 +35,47 @@
#include <fennec/langproc/strings/string.h> #include <fennec/langproc/strings/string.h>
#include <fennec/scene/component.h> #include <fennec/scene/component.h>
#include <utility>
namespace fennec namespace fennec
{ {
struct scene_node_ref {
size_t scene;
size_t id;
};
struct scene_node : typed<scene_node> { struct scene_node : typed<scene_node> {
const size_t scene;
const size_t id; // Public Members ======================================================================================================
public:
union {
struct {
const size_t scene;
const size_t id;
};
const scene_node_ref ref;
};
string name; string name;
scene_node(size_t id, size_t scene, const string& name) scene_node(size_t scene, size_t id, string name)
: id(id), scene(scene), name(name) { : scene(scene), id(id), name(std::move(name)) {
} }
template<typename ComponentT>
component* add_component() {
_components.push_back(component::create<ComponentT>(scene, id));
return _components.back();
}
component* add_component(uint64_t type) {
_components.push_back(component::create(type, scene, id));
return _components.back();
}
private: private:
dynarray<component_t> _components; dynarray<component*> _components;
}; };
} }

View File

@@ -24,7 +24,7 @@
namespace fennec namespace fennec
{ {
STATIC_CONSTRUCTOR(_init_linux) { FENNEC_STATIC_CONSTRUCTOR(_init_linux) {
static linux_platform platform; static linux_platform platform;
} }

View File

@@ -48,7 +48,7 @@ int main(int, char **)
fennec::test::fennec_test_math(); fennec::test::fennec_test_math();
fennec_test_spacer(3); fennec_test_spacer(3);
fennec_test_header("format processing library"); fennec_test_header("language processing library");
fennec_test_spacer(2); fennec_test_spacer(2);
fennec::test::fennec_test_langproc(); fennec::test::fennec_test_langproc();
fennec_test_spacer(3); fennec_test_spacer(3);

View File

@@ -40,7 +40,8 @@ namespace test
inline void fennec_test_langproc_format() { inline void fennec_test_langproc_format() {
fennec_test_run(fennec::format("{}", "Hello World!"), string("Hello World!")); fennec_test_run(fennec::format("{}", "Hello World!"), string("Hello World!"));
fennec_test_run(fennec::format("{0}", "Hello World!"), string("Hello World!"));
} }
} }