Yorum:
Artemis uygulaması ilginçtir. Benzer bir çözüm buldum; bileşenlerimi "Öznitelikler" ve "Davranışlar" olarak adlandırmam dışında. Bu bileşen türlerini ayırma yaklaşımı benim için çok iyi çalıştı.
Çözümle ilgili olarak:
Kod kullanımı kolaydır, ancak C ++ ile deneyimli değilseniz uygulamanın izlenmesi zor olabilir. Yani...
İstenen arayüz
Yaptığım, tüm bileşenlerin merkezi bir havuzuna sahip olmak. Her bileşen türü belirli bir dizeyle eşleştirilir (bileşen adını temsil eder). Sistemi böyle kullanıyorsunuz:
// 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);
}
Hayata geçirme
Uygulama o kadar da kötü değil, ama yine de oldukça karmaşık; bazı şablonlar ve işlev işaretçileri hakkında bilgi gerektirir.
Not: Joe Wreschnig, yorumlarda, özellikle önceki uygulamamın derleyicinin kodu optimize etme konusunda ne kadar iyi olduğu konusunda çok fazla varsayımda bulunduğunu; sorun zararlı değildi, imo, ama beni de rahatsız etti. Ayrıca eski COMPONENT_REGISTER
makronun şablonlarla çalışmadığını da fark ettim .
Kodu değiştirdim ve şimdi tüm bu sorunların giderilmesi gerekiyor. Makro şablonlarla çalışır ve Joe'nun gündeme getirdiği konular ele alınmıştır: şimdi derleyicilerin gereksiz kodu en iyi duruma getirmeleri çok daha kolaydır.
bileşen / component.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
bileşen / 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
bileşen / component.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;
}
Lua ile genişletme
Biraz çalışmakla (çok zor değil) bunun C ++ veya Lua'da tanımlanan bileşenlerle sorunsuz bir şekilde çalışmak için kullanılabileceğini, bunun hakkında hiç düşünmek zorunda olmadığını belirtmeliyim.