Một nhận xét:
Việc thực hiện Artemis rất thú vị. Tôi đã đưa ra một giải pháp tương tự, ngoại trừ tôi gọi các thành phần của mình là "Thuộc tính" và "Hành vi". Cách tiếp cận tách các loại thành phần này đã làm việc rất độc đáo đối với tôi.
Về giải pháp:
Mã rất dễ sử dụng, nhưng việc triển khai có thể khó theo dõi nếu bạn không có kinh nghiệm với C ++. Vì thế...
Giao diện mong muốn
Những gì tôi đã làm là có một kho lưu trữ trung tâm của tất cả các thành phần. Mỗi loại thành phần được ánh xạ tới một chuỗi nhất định (đại diện cho tên thành phần). Đây là cách bạn sử dụng hệ thống:
// 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);
}
Việc thực hiện
Việc thực hiện không phải là xấu, nhưng nó vẫn khá phức tạp; nó đòi hỏi một số kiến thức về mẫu và con trỏ hàm.
Lưu ý: Joe Wreschnig đã đưa ra một số điểm tốt trong các nhận xét, chủ yếu là về cách triển khai trước đây của tôi đưa ra quá nhiều giả định về việc trình biên dịch tốt như thế nào trong việc tối ưu hóa mã; vấn đề không gây bất lợi, imo, nhưng nó cũng làm tôi khó chịu. Tôi cũng nhận thấy rằng COMPONENT_REGISTER
macro trước đây không hoạt động với các mẫu.
Tôi đã thay đổi mã và bây giờ tất cả các vấn đề đó sẽ được khắc phục. Macro hoạt động với các mẫu và các vấn đề mà Joe nêu ra đã được giải quyết: giờ đây trình biên dịch dễ dàng hơn nhiều để tối ưu hóa mã không cần thiết.
thành phần / thành phần.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
thành phần / chi tiết.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
thành phần / thành phần.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;
}
Mở rộng với Lua
Tôi nên lưu ý rằng với một chút công việc (không khó lắm), điều này có thể được sử dụng để làm việc liền mạch với các thành phần được xác định trong C ++ hoặc Lua, mà không cần phải suy nghĩ về nó.