Sebuah komentar:
Implementasi Artemis menarik. Saya datang dengan solusi yang serupa, kecuali saya menyebut komponen saya "Atribut" dan "Perilaku". Pendekatan pemisahan jenis komponen ini telah bekerja sangat baik untuk saya.
Mengenai solusinya:
Kode ini mudah digunakan, tetapi implementasinya mungkin sulit diikuti jika Anda tidak berpengalaman dengan C ++. Begitu...
Antarmuka yang diinginkan
Apa yang saya lakukan adalah memiliki repositori pusat dari semua komponen. Setiap tipe komponen dipetakan ke string tertentu (yang mewakili nama komponen). Ini adalah bagaimana Anda menggunakan sistem:
// 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);
}
Pelaksanaan
Implementasinya tidak terlalu buruk, tetapi masih cukup rumit; membutuhkan pengetahuan tentang template dan pointer fungsi.
Catatan: Joe Wreschnig telah membuat beberapa poin bagus dalam komentar, terutama tentang bagaimana implementasi saya sebelumnya membuat terlalu banyak asumsi tentang seberapa bagus kompiler dalam mengoptimalkan kode; masalah ini tidak merugikan, imo, tapi itu memang mengganggu saya juga. Saya juga memperhatikan bahwa COMPONENT_REGISTER
makro sebelumnya tidak berfungsi dengan templat.
Saya telah mengubah kode dan sekarang semua masalah itu harus diperbaiki. Makro bekerja dengan templat dan masalah yang diangkat Joe telah ditangani: sekarang jauh lebih mudah bagi kompiler untuk mengoptimalkan kode yang tidak perlu.
komponen / komponen.h
#ifndef COMPONENT_COMPONENT_H
#define COMPONENT_COMPONENT_H
// Standard libraries
#include <string>
// Custom libraries
#include "detail.h"
class Component
{
// ...
};
namespace component
{
Component* create(const std::string& name);
void destroy(const Component* comp);
}
#define COMPONENT_REGISTER(TYPE, NAME) \
namespace component { \
namespace detail { \
namespace \
{ \
template<class T> \
class ComponentRegistration; \
\
template<> \
class ComponentRegistration<TYPE> \
{ \
static const ::component::detail::RegistryEntry<TYPE>& reg; \
}; \
\
const ::component::detail::RegistryEntry<TYPE>& \
ComponentRegistration<TYPE>::reg = \
::component::detail::RegistryEntry<TYPE>::Instance(NAME); \
}}}
#endif // COMPONENT_COMPONENT_H
komponen / detail.h
#ifndef COMPONENT_DETAIL_H
#define COMPONENT_DETAIL_H
// Standard libraries
#include <map>
#include <string>
#include <utility>
class Component;
namespace component
{
namespace detail
{
typedef Component* (*CreateComponentFunc)();
typedef std::map<std::string, CreateComponentFunc> ComponentRegistry;
inline ComponentRegistry& getComponentRegistry()
{
static ComponentRegistry reg;
return reg;
}
template<class T>
Component* createComponent() {
return new T;
}
template<class T>
struct RegistryEntry
{
public:
static RegistryEntry<T>& Instance(const std::string& name)
{
// Because I use a singleton here, even though `COMPONENT_REGISTER`
// is expanded in multiple translation units, the constructor
// will only be executed once. Only this cheap `Instance` function
// (which most likely gets inlined) is executed multiple times.
static RegistryEntry<T> inst(name);
return inst;
}
private:
RegistryEntry(const std::string& name)
{
ComponentRegistry& reg = getComponentRegistry();
CreateComponentFunc func = createComponent<T>;
std::pair<ComponentRegistry::iterator, bool> ret =
reg.insert(ComponentRegistry::value_type(name, func));
if (ret.second == false) {
// This means there already is a component registered to
// this name. You should handle this error as you see fit.
}
}
RegistryEntry(const RegistryEntry<T>&) = delete; // C++11 feature
RegistryEntry& operator=(const RegistryEntry<T>&) = delete;
};
} // namespace detail
} // namespace component
#endif // COMPONENT_DETAIL_H
komponen / komponen.cpp
// Matching header
#include "component.h"
// Standard libraries
#include <string>
// Custom libraries
#include "detail.h"
Component* 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 nullptr;
}
detail::CreateComponentFunc func = it->second;
return func();
}
void component::destroy(const Component* comp)
{
delete comp;
}
Memperluas dengan Lua
Saya harus mencatat bahwa dengan sedikit kerja (ini tidak terlalu sulit), ini dapat digunakan untuk bekerja dengan mulus dengan komponen yang didefinisikan dalam C ++ atau Lua, tanpa harus memikirkannya.