Reputation: 133
How would it be possible to solve a circular dependency caused by template functions?
For example, I have an Engine class defined that stores a list of Entities, and is responsible for creating entities and adding / removing components from them.
class Engine
{
public:
Entity CreateEntity();
template <typename T, typename... Args>
void AddComponentToEntity(Entity& entity, Args... args)
{
// Code to add component to entity
}
template <typename T>
void RemoveComponentFromEntity(Entity& entity)
{
// Code to remove component from entity
}
private:
std::vector<Entity> entities;
};
I have also then added functions within the entity class to "wrap" these functions, allowing a nice syntax for adding components to entities.
class Entity
{
public:
template <typename T, typename... Args>
void AddComponent(Args... args)
{
engine->AddComponentToEntity<T>(*this, args...);
}
template <typename T>
void RemoveComponent()
{
engine->RemoveComponentFromEntity<T>(*this);
}
private:
Engine* engine;
};
This allows me to write code like this
entity.AddComponent<PhysicsComponent>(Arguments..);
Instead of having to reference the engine object directly everywhere
engine->AddComponentToEntity(entity, Arguments..);
However, as the engine class contains entity instances, it has to include the Entity class. The Entity class then has to include the Engine class in order for the template functions to call the methods, which causes a circular dependency.
This could easily be solved if the functions were not templates, as the implementation could be put in Entity.cpp and then I could include the Engine class there. I am struggling to see how the same could be done with template functions.
Upvotes: 0
Views: 146
Reputation: 14360
Your main problem here is the design of your classes. It's pointless that an engine
which is contained for an entity
carry with the responsability of adding components to such entity
.
If you have different behaviours at the time of adding components, depending of Engine, well, you could make Engine a parameter for your Entity::AddComponent
function:
template <typename Engine, typename T, typename... Args>
void AddComponent(Engine engine, Args... args)
{
engine->AddComponentToEntity<T>(*this, args...);
}
On the other hand you could use the idiom Curiously recurring template pattern and move the member Engine::entities
to Entity::entities
(or Entity::children
if your are trying to do some kind of tree data structure).
template <typename Entity>
class Engine
{
public:
Engine(){}
void addComponent(Entity entity, ...){}
}
class Entity: public Engine<Entity>
{
// Now this class has the function addComponent
// just like defined in Engine.
private:
std::vector<Entity> children; // Here this has more sense.
};
If you need different tipes of engines, just derive from Engine
and specialize/add what needs to be specialized/added.
Upvotes: 0
Reputation: 5249
You can resolve it by writing the class definition first, then the function implementation. For example:
class Engine;
class Entity
{
public:
template <typename T, typename... Args>
void AddComponent(Args... args);
template <typename T>
void RemoveComponent();
private:
Engine* engine;
};
class Engine
{
public:
Entity CreateEntity();
template <typename T, typename... Args>
void AddComponentToEntity(Entity& entity, Args... args);
template <typename T>
void RemoveComponentFromEntity(Entity& entity);
private:
std::vector<Entity> entities;
};
template <typename T, typename... Args>
void Entity::AddComponent(Args... args)
{
engine->AddComponentToEntity<T>(*this, args...);
}
template <typename T>
void Entity::RemoveComponent()
{
engine->RemoveComponentFromEntity<T>(*this);
}
void Engine::AddComponentToEntity(Entity& entity, Args... args)
{
// Code to add component to entity
}
template <typename T>
void Engine::RemoveComponentFromEntity(Entity& entity)
{
// Code to remove component from entity
}
This way you don't have to split your files.
Upvotes: 0
Reputation: 218138
You may do mostly as if the function were not template by splitting definition from declaration:
// Engine.h
#ifndef ENGINE_H
#define ENGINE_H
#include <vector>
class Entity; // forward declaration
class Engine
{
public:
Entity CreateEntity();
template <typename T, typename... Args>
void AddComponentToEntity(Entity& entity, Args... args);
template <typename T>
void RemoveComponentFromEntity(Entity& entity);
private:
std::vector<Entity> entities;
};
#include "engine.inl"
#endif
// Engine.inl
#ifndef ENGINE_INL
#define ENGINE_INL
#include "engine.h"
#include "entity.h"
template <typename T, typename... Args>
void Engine::AddComponentToEntity(Entity& entity, Args... args)
{
// Implementation.
}
template <typename T>
void Engine::RemoveComponentFromEntity(Entity& entity)
{
// Implementation.
}
#endif
And similarly for entity.h/entity.inl.
Upvotes: 0