A comment:
The Artemis implementation is interesting. I came up with a similar solution, except I called my components "Attributes" and "Behaviors". This approach of separating types of components has worked very nicely for me.
Regarding the solution:
The code is easy to use, but the implementation might be hard to follow if you're not experienced with C++. So...
The desired interface
What I did is to have a central repository of all components. Each component type is mapped to a certain string (which represents the component name). This is how you use the system:
// Every time you write a new component class you have to register it.
// For that you use the `COMPONENT_REGISTER` macro.
class RenderingComponent : public Component
{
// Bla, bla
};
COMPONENT_REGISTER(RenderingComponent, "RenderingComponent")
int main()
{
// To then create an instance of a registered component all you have
// to do is call the `create` function like so...
Component* comp = component::create("RenderingComponent");
// I found that if you have a special `create` function that returns a
// pointer, it's best to have a corresponding `destroy` function
// instead of using `delete` directly.
component::destroy(comp);
}
The implementation
The implementation is not that bad, but it's still pretty complex; it requires some knowledge of templates and function pointers.
The header file
#include <map>
#include <string>
class Component
{
};
namespace component
{
Component* create(const std::string& name);
void destroy(const Component* comp);
// This namespace contains implementation details.
namespace detail
{
template<class T>
Component* createComponent()
{
return new T;
}
typedef std::map<std::string, Component* (*)()> ComponentRegistry;
ComponentRegistry& getComponentRegistry();
template<class T>
struct RegistryEntry
{
RegistryEntry(const std::string& name)
{
ComponentRegistry& reg = getComponentRegistry();
reg[name] = createComponent<T>;
}
};
} // namespace detail
} // namespace component
#define COMPONENT_REGISTER(TYPE, NAME) \
namespace { \
static const component::detail::RegistryEntry<TYPE> reg_ent_##TYPE(NAME); \
}
The source file
#include "component.h"
#include <string>
namespace component
{
namespace detail
{
ComponentRegistry& getComponentRegistry()
{
static ComponentRegistry reg;
return reg;
}
}
Component* create(const std::string& name)
{
detail::ComponentRegistry& reg = detail::getComponentRegistry();
detail::ComponentRegistry::iterator it = reg.find(name);
if (it == reg.end()) {
// This happens when there is no component registered to this
// name. Here I return a null pointer, but you can handle this
// error differently if it suits you better.
return 0;
}
return it->second();
}
void destroy(const Component* comp)
{
delete comp;
}
} // namespace component
Extending with Lua
I should note that with a bit of work (it's not very hard), this can be used to seamlessly work with components defined in either C++ or Lua, without ever having to think about it.