C++ template function causes circular dependancy

147 Views Asked by At

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.

3

There are 3 best solutions below

0
On

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.

0
On

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.

0
On

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.