- 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 ========================================================================================================
${FENNEC_EXTRA_SOURCES}
include/fennec/gfx3d/mesh_instance.h
)
add_dependencies(fennec metaprogramming fennec-dependencies)

View File

@@ -18,7 +18,7 @@
# 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")

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
// Helper for running a function before main()
#define STATIC_CONSTRUCTOR(f) \
#define FENNEC_STATIC_CONSTRUCTOR(f) \
inline static void f(void); \
struct f##_t_ { inline f##_t_(void) { f(); } }; inline static f##_t_ f##_; \
inline static void f(void)

View File

@@ -54,6 +54,11 @@ public:
typed(TypeT*)
: 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:
static constexpr size_t npos = -1;
enum token_ : uint8_t {
token_text = 0,
token_param,
enum token_ : bool {
token_text = false,
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) {
while (*start != c && *start != '\0') {
@@ -80,47 +84,64 @@ private:
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 ===========================================================================================
public:
format_string() = delete;
format_string(const char (&str)[N])
constexpr format_string(const char (&str)[N])
: _tokens(), _num_tokens(0) {
static_assert(is_constant_evaluated(), "Runtime format strings are not supported.");
// TODO: Numbered Parameters
size_t params = 0;
for (size_t i = 0; i < N; ++i) {
size_t n = this->find(str + i, '{') - str;
for (size_t i = 0; i < N - 1; ++i) {
size_t l = this->find(str + i, '{') - str;
// Push the current token
if (n - i > 0) {
_tokens[_num_tokens++] = { token_text, { &str[i], n - i } };
if (l - i > 0) {
_tokens[_num_tokens++] = { token_text, { &str[i], l - i }, npos };
}
// no more braces, break
if (n >= N) {
if (l >= N) {
break;
}
// escaped brace
if (str[n + 1] == '{') {
_tokens[_num_tokens++] = { token_text, { &str[n], 1 } };
i = n + 1;
if (str[l + 1] == '{') {
_tokens[_num_tokens++] = { token_text, { &str[l], 1 }, npos };
i = l + 1;
continue;
}
size_t e = this->find(str + n, '}') - str;
//static_assert(e >= N, "Malformed format string, mismatched brace");
size_t e = this->find(str + l, '}') - str;
assertd(e < N, "Malformed format string, mismatched brace");
_tokens[_num_tokens++] = { token_param, { &str[n], e - n + 1 } };
++params;
i = e;
// Colon found
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);
}
//static_assert(params == sizeof...(ArgsT), "Malformed format string, parameter count does not match argument count.");
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;
}
}
constexpr format_string(const format_string&) = default;
@@ -129,33 +150,48 @@ public:
~format_string() = default;
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:
array<token, N> _tokens;
size_t _num_tokens;
string _format(size_t) const {
return string{""};
void _format(size_t) const {
}
template<typename HeadT, typename...RestT>
string _format(size_t i, HeadT&& head, RestT&&...rest) const {
string res;
for (; i < _num_tokens; ++i) {
const auto& token = _tokens[i];
if (token.first == token_param) {
formatter<HeadT> fmt;
res += fmt(token.second, head);
res += this->_format(i + 1, fennec::forward<RestT>(rest)...);
void _format(size_t p, string (&parts)[N], HeadT&& head, RestT&&...rest) const {
size_t i = 0;
for (const token& tok : _tokens) {
if (i >= _num_tokens) {
break;
}
res += token.second;
if (tok.param == p) {
formatter<HeadT> fmt;
parts[i] = fmt(tok.view, head);
}
return res;
++i;
}
this->_format(p + 1, fennec::forward<RestT>(rest)...);
}
};

View File

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

View File

@@ -21,30 +21,27 @@
#include <fennec/containers/dynarray.h>
#include <fennec/containers/map.h>
#include <fennec/containers/object_pool.h>
#include <fennec/containers/optional.h>
#include <fennec/containers/rdtree.h>
#include <fennec/lang/startup.h>
#include <fennec/langproc/strings/string.h>
#include <fennec/lang/typed.h>
#include <fennec/langproc/filesystem/path.h>
#define FENNEC_REGISTER_COMPONENT(T, p) \
FENNEC_STATIC_CONSTRUCTOR(T) { \
component::register_type<T>(p) \
}
namespace fennec
{
struct component_t {
uint64_t type;
size_t id;
};
class component : public typed<component> {
public:
// TYPEDEFS & CONSTANTS ================================================================================================
using component_create = size_t (*)(size_t, size_t);
using component_get = component* (*)(size_t);
using component_destroy = void (*)(size_t);
using component_tick = void (*)(size_t, double);
using component_frame = void (*)(size_t, uint64_t);
using component_create = component* (*)(size_t, size_t);
using component_destroy = void (*)(component*);
// TYPE OPERATIONS =====================================================================================================
@@ -52,31 +49,22 @@ public:
struct typeinfo {
string name;
component_create create;
component_destroy destroy;
component_get get;
component_tick tick;
component_frame frame;
typeinfo()
: name("")
, create(nullptr), destroy(nullptr)
, get(nullptr)
, tick(nullptr), frame(nullptr) {
, create(nullptr) {
}
typeinfo(const string& name,
component_create create, component_destroy destroy,
component_get get,
component_tick tick, component_frame frame)
component_create create)
: name(name)
, create(create), destroy(destroy), get(get)
, tick(tick), frame(frame) {
, create(create) {
}
};
struct typeentry {
string name;
uint64_t id;
uint64_t type;
};
using typelist_t = dynarray<optional<typeinfo>>;
@@ -93,14 +81,12 @@ private:
static constexpr size_t root = typetree_t::root;
static void _register_type( uint64_t id, const path& path,
component_create create, component_destroy destroy,
component_get get,
component_tick tick, component_frame frame) {
component_create create) {
// Register the type
if (id > _typelist.size()) {
_typelist.resize(id + 1);
}
_typelist[id] = typeinfo{ path.filename(), create, destroy, get, tick, frame };
_typelist[id] = typeinfo{ path.filename(), create };
// Create tree entry
size_t node = root;
@@ -112,7 +98,7 @@ private:
node = _typetree.child(parent);
while (node != npos && _typetree[node].id != id) {
while (node != npos && _typetree[node].type != id) {
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
/// \tparam ComponentT Type of the component
@@ -159,60 +118,39 @@ private:
/// \returns The component created by this function
template<typename ComponentT>
static constexpr component* default_create(size_t scene, size_t node) {
auto& storage = _type_storage<ComponentT>();
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];
return new ComponentT(scene, node);
}
// Public Registry =====================================================================================================
public:
///
/// \brief Get an uuid for the specified component type
/// \tparam ComponentT The component type
/// \returns An uuid that is associated with the type
static constexpr const typetree_t& typelist() {
return _typetree;
}
template<typename ComponentT>
static uint64_t uuid() {
return typeuuid<ComponentT, component>();
static constexpr const typeinfo& type_info() {
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
/// \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 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>
static void register_type(const cstring& name,
component_tick tick, component_frame frame,
component_create create = default_create<ComponentT>,
component_get get = default_get<ComponentT>,
component_destroy destroy = default_destroy<ComponentT>) {
component::_register_type(uuid<ComponentT>(), name, create, destroy, get, tick, frame);
static void register_type(const path& path,
component_create create = default_create<ComponentT>) {
component::_register_type(id<ComponentT>(), path, create);
}
///
@@ -220,69 +158,22 @@ public:
/// \param type The type to create
/// \param node The node to associate the component with
/// \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];
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
/// \param node The node to associate the component with
/// \returns The created component
template<typename ComponentT>
static size_t create(size_t scene, size_t node) {
auto& typei = _typelist[uuid<ComponentT>()];
static component* create(size_t scene, size_t node) {
auto& typei = _typelist[id<ComponentT>()];
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 =============================================================================================================
public:
const size_t node;

View File

@@ -35,20 +35,47 @@
#include <fennec/langproc/strings/string.h>
#include <fennec/scene/component.h>
#include <utility>
namespace fennec
{
struct scene_node_ref {
size_t scene;
size_t id;
};
struct scene_node : typed<scene_node> {
// Public Members ======================================================================================================
public:
union {
struct {
const size_t scene;
const size_t id;
};
const scene_node_ref ref;
};
string name;
scene_node(size_t id, size_t scene, const string& name)
: id(id), scene(scene), name(name) {
scene_node(size_t scene, size_t id, string 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:
dynarray<component_t> _components;
dynarray<component*> _components;
};
}

View File

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

View File

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

View File

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