Как было бы возможно решить круговую зависимость, вызванную функциями шаблона?
Например, у меня определен класс Engine, который хранит список сущностей и отвечает за создание сущностей и добавление / удаление компонентов из них.
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;
};
Затем я добавил функции в классе сущностей, чтобы «обернуть» эти функции, предоставив хороший синтаксис для добавления компонентов к сущностям.
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;
};
Это позволяет мне писать такой код
entity.AddComponent<PhysicsComponent>(Arguments..);
Вместо того, чтобы ссылаться на объект двигателя напрямую везде
engine->AddComponentToEntity(entity, Arguments..);
Однако, поскольку класс двигателя содержит экземпляры сущности, он должен включать в себя класс сущности. Затем класс Entity должен включать класс Engine, чтобы функции шаблона вызывали методы, что вызывает циклическую зависимость.
Это легко может быть решено, если функции не являются шаблонами, так как реализация может быть помещена в Entity.cpp, а затем я могу включить туда класс Engine. Я изо всех сил пытаюсь понять, как то же самое можно сделать с помощью шаблонных функций.
Вы можете сделать в основном так, как если бы функция не была шаблоном, отделив определение от объявления:
// 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
И аналогично для entity.h/entity.inl.
Вы можете решить это, написав сначала определение класса, а затем реализацию функции. Например:
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
}
Таким образом, вам не нужно разбивать свои файлы.
Ваша главная проблема здесь — это дизайн ваших классов. Бессмысленно, что engine
который содержится для entity
нести ответственность за добавление компонентов к таким entity
,
Если во время добавления компонентов вы по-разному себя ведете, в зависимости от Engine, вы можете сделать Engine параметром для вашего Entity::AddComponent
функция:
template <typename Engine, typename T, typename... Args>
void AddComponent(Engine engine, Args... args)
{
engine->AddComponentToEntity<T>(*this, args...);
}
С другой стороны, вы можете использовать идиому Любопытно повторяющийся шаблон и переместить член Engine::entities
в Entity::entities
(или же Entity::children
если вы пытаетесь сделать какую-то древовидную структуру данных).
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.
};
Если вам нужны разные типы двигателей, просто производные от Engine
и специализировать / добавить то, что нужно специализировать / добавить.