Templates and runtime component loading in a component-based game engine
Suppose I have an component based game engine, and in that engine I have an object that stores a repository of properties in a form of a stl map and can be accessed through the object access() method, which is a templated call so you can access and store any type of data into the repository, eg:
class component { private: object * parent; public: component() : parent(0) { } void setparent(object * p) { parent = p; } virtual void tick() = 0; } class object { private: std::list compipeline; public: template<typename prop_t> access(std::string propname) { static std::map<std::string, prop_t> repository; return repository; } void attach(component * c) { compipelane.push_back(c); c->setparent(this); } };
In this schema, components will call something like
parent.access<double>("health) = 0;
Thats 开发者_C百科fine so far but suppose I want to support dynamic loading of components. I know that templates are resolved in compile time, then all code for access() calls like the one above will be generated when my engine code is compiled. But then I develop a component and compile it as a loadable shared library that does an parent.access("damage"), eg:
class health : public component { public: health(object & parent) : component(parent) { } virtual void tick() { double damage = parent.access<double>("damage"); parent.access<double>("health") -= damage; parent.access<double>("damage") = 0; } }
Well then begins my sea of questions and doubts: Will this code compile as a shared library? If yes, what will happen when I load it at runtime and plug in a already created and populated with other components object? Suppose other components that already made the access<double>(...) call, will the map be the same?
Suppose the component also had a type created, like struct position { int x,y; }
that was not defined before so this code hasn't been compiled in the main engine,
what will happen in access<position>(...)
in this recently-plugged component?
Sorry about the big question, but its a big question in my head also. I am fairly familiar with C++ but still understanding how templates works...
Does anybody have experience with this that could enlighten me?
Also I feel that a static std::map inside a function call is not the best approach but I cannot think of another that will let me create spans of different property repositories based on a call...
Thank you!
Besides of the wrong signature of access
in your code, it all boils down to how your platform deals with instantiating global objects from shared libraries. As I see it, you will end up with different instances of std::map<std::string, Whatever>
per library loaded into process...
Doing that is fine. In fact because of link order issues and construction order issues you want to have your templatized container as a static in a function. The rules state that all static objects must be created before the first function is called. I use a function called GetRegistry that only holds the registry since multiple functions cannot access other functions statics. So you can have something like this:
class GameWorld
{
template <typename T>
T& GetRegistry
{
static T registry;
return registry;
}
public:
template <typename T>
void Add(const T& gameThing)
{
GetRegistry<T>().push_back(gameThing);
}
template <typename T>
void Update()
{
for_each(GetRegistry<T>().begin(), GetRegitry<T>.end(), Render);
}
};
void main()
{
GameWorld world;
Animal cow;
Soldier sniper;
Soldier cook;
world.Add(cow);
world.Add(sniper);
world.Add(cook);
world.Update<Animal>();
world.Update<Soldier>();
}
精彩评论