Last active
November 27, 2020 17:58
-
-
Save qis/05967d8fffecee346a8705cee0303842 to your computer and use it in GitHub Desktop.
C++ component system with a singleton registry.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ============================================================================ | |
// component.hpp | |
// ============================================================================ | |
#pragma once | |
#include <typeindex> | |
#include <cstddef> | |
class component_manager { | |
template <typename T> | |
friend class component; | |
public: | |
// Create component objects. | |
static std::size_t create(); | |
// Create component objects with given name. | |
static std::size_t create(const char* name); | |
// Destroy component objects. | |
static void destroy(); | |
// Clear component registry. | |
static void clear(); | |
private: | |
static void add(std::type_index index, const char* name, void* (*create)(), void (*deleter)(void*)); | |
static void* get(std::type_index index); | |
}; | |
template <typename T> | |
class component { | |
public: | |
// Registers component. | |
component(const char* name) { | |
component_manager::add( | |
typeid(T), | |
name, | |
[]() { | |
return reinterpret_cast<void*>(new T()); | |
}, | |
[](void* o) { | |
delete reinterpret_cast<T*>(o); | |
}); | |
} | |
// Returns component object. | |
static T* get() { | |
return reinterpret_cast<T*>(component_manager::get(typeid(T))); | |
} | |
}; | |
// ============================================================================ | |
// component.cpp | |
// ============================================================================ | |
#include "component.hpp" | |
#include <algorithm> | |
#include <string> | |
#include <unordered_map> | |
#include <cassert> | |
namespace { | |
struct entry { | |
std::string name; | |
void* object = nullptr; | |
void* (*create)(); | |
void (*deleter)(void*); | |
}; | |
std::unordered_map<std::type_index, entry>& components() { | |
static struct registry { | |
~registry() { | |
component_manager::clear(); | |
} | |
std::unordered_map<std::type_index, entry> components; | |
} registry; | |
return registry.components; | |
} | |
} // namespace | |
std::size_t component_manager::create() { | |
auto& c = components(); | |
for (auto& e : c) { | |
if (!e.second.object) { | |
assert(e.second.create); | |
assert(e.second.deleter); | |
e.second.object = e.second.create(); | |
} | |
} | |
return c.size(); | |
} | |
std::size_t component_manager::create(const char* name) { | |
const auto search = [name](const auto& e) { | |
return e.second.name == name; | |
}; | |
auto& c = components(); | |
const auto next = [&c, &search](auto begin) { | |
return std::find_if(begin, c.end(), search); | |
}; | |
std::size_t count = 0; | |
for (auto it = next(c.begin()); it != c.end(); it = next(++it)) { | |
if (!it->second.object) { | |
assert(it->second.create); | |
assert(it->second.deleter); | |
it->second.object = it->second.create(); | |
} | |
count++; | |
} | |
return count; | |
} | |
void component_manager::destroy() { | |
for (auto& e : components()) { | |
if (!e.second.object) { | |
assert(e.second.deleter); | |
e.second.deleter(e.second.object); | |
e.second.object = nullptr; | |
} | |
} | |
} | |
void component_manager::clear() { | |
destroy(); | |
components().clear(); | |
} | |
void component_manager::add(std::type_index index, const char* name, void* (*create)(), void (*deleter)(void*)) { | |
auto& e = components()[index]; | |
if (e.object) { | |
assert(e.deleter); | |
e.deleter(e.object); | |
e.object = nullptr; | |
} | |
assert(name); | |
e.name = name; | |
e.create = create; | |
e.deleter = deleter; | |
} | |
void* component_manager::get(std::type_index index) { | |
auto& c = components(); | |
if (const auto it = c.find(index); it != c.end()) { | |
if (!it->second.object) { | |
assert(it->second.create); | |
assert(it->second.deleter); | |
it->second.object = it->second.create(); | |
} | |
return it->second.object; | |
} | |
return nullptr; | |
} | |
// ============================================================================ | |
// example1.hpp | |
// ============================================================================ | |
#pragma once | |
class example1 { | |
public: | |
int value1(); | |
}; | |
// ============================================================================ | |
// example1.cpp | |
// ============================================================================ | |
#include "example1.hpp" | |
#include <component.hpp> | |
int example1::value1() { | |
return 1; | |
} | |
component<example1> example1_component("example1"); | |
// ============================================================================ | |
// example2.hpp | |
// ============================================================================ | |
#pragma once | |
class example2 { | |
public: | |
example2(); | |
int value2(); | |
}; | |
// ============================================================================ | |
// example2.cpp | |
// ============================================================================ | |
#include "example2.hpp" | |
#include <component.hpp> | |
example2::example2() { | |
// Make sure that the example1 component is created first. | |
component_manager::create("example1"); | |
} | |
int example2::value2() { | |
return 2; | |
} | |
component<example2> example2_component("example2"); | |
// ============================================================================ | |
// main.cpp | |
// ============================================================================ | |
#include <component.hpp> | |
#include <example1.hpp> | |
#include <example2.hpp> | |
#include <string> | |
#include <cstdio> | |
int main() { | |
// Create components. | |
component_manager::create(); | |
// Use components. | |
std::puts(std::to_string(component<example1>::get()->value1()).data()); | |
std::puts(std::to_string(component<example2>::get()->value2()).data()); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment