Я экспериментировал с использованием шаблонов переменных для реализации класса GameObject с использованием Mixins (в сочетании с CRTP для статического полиморфизма). У меня все работает, но GameObject
«s getComponent()
Метод требует как типа, так и индекса, что является громоздким. Можно getComponent()
переписать так, чтобы ему нужен был только тип ИЛИ индекс компонента, который я хочу?
Вот код для компонентов, которые составляют Mixins:
class ComponentBase {
public:
virtual void update () = 0;
};
template <class T>
class Component : public ComponentBase {
public:
virtual void update () {
static_cast<T*>(this)->update();
}
};
class TransformComponent : public Component<TransformComponent> {
public:
void update () {
// ...
}
// ...
protected:
int _x, _y;
};
class ColliderComponent : public Component<ColliderComponent> {
public:
void update () {
// ...
}
// ...
protected:
bool _collided;
};
И тогда вот GameObject
Mixin класс:
template <class ... Mixins>
class GameObject : private Mixins...
{
public:
GameObject (const Mixins&... mixins) : Mixins(mixins)..., _components{&mixins...} {}
GameObject (Mixins&&... mixins) : Mixins(std::forward<Mixins>(mixins))..., _components{&mixins...} {}
constexpr size_t getNumComponents () const { return _numComponents; };
template <typename T, int index>
constexpr T* getComponent () const {
static_assert(index < _numComponents, "getComponent: index out of range");
return static_cast<T*>(_components[index]);
}
void update () {
for (size_t i = 0; i < getNumComponents(); ++i) {
_components[i]->update();
}
}
protected:
static const size_t _numComponents = sizeof...(Mixins);
ComponentBase* _components[sizeof...(Mixins)];
};
Я думал об использовании автоматического возврата значения, как в:
template <int index>
constexpr auto getComponent() const -> decltype(_components[index]) {
return _components[index];
}
но decltype
не может оценить выражение в объявлении метода.
Я также пытался использовать рекурсию шаблона для getComponent()
принимая тип компонента, но вы не можете специализировать метод класса в неспециализированном шаблонном классе.
т.е .:
template <typename T>
constexpr T* getComponent () const {
// If T equals the type of _components[index], then return, else
// call getComponent<T, index - 1>().
}
Я довольно новичок в работе с TMP, поэтому любая помощь приветствуется.
Прежде всего, я думаю, что здесь что-то идет не так:
GameObject (const Mixins&... mixins) : Mixins(mixins)..., _components{&mixins...} {}
GameObject (Mixins&&... mixins) : Mixins(std::forward<Mixins>(mixins))..., _components{&mixins...} {}
Похоже, вы хотите сохранить данные объекты в вашем объекте mixin как частные базовые классы, но вы берете указатели на исходные объекты для вашего _components
член. Если исходные объекты перемещаются, вы даже берете указатели на объекты, которые, скорее всего, перемещены.
Из-за этого мне не на 100% ясно, что вы действительно хотите, но вот решение, основанное на std::tuple
вместо множественного частного наследования:
http://coliru.stacked-crooked.com/a/178be711a9a62f2b
Как видите, нам не нужен общий базовый класс. Все типы, которые предлагают update
Метод поддерживается миксином. Конечно, вы можете изменить его так, чтобы он требовал базового класса, если он вам нужен в другом месте.
GetComponent<T>()
Метод, которому нужен только тип, и возвращает указатель на соответствующий компонент (на основе небольшого упрощенного кода):
#include <iostream>
#include <type_traits>
#include <iostream>
template <typename T, int N, typename... Mixins>
struct get_index
{
static constexpr int value = -1;
};
template <typename T, int N, typename... Mixins>
struct get_index<T, N, T, Mixins...>
{
static constexpr int value = N;
};
template <typename T, int N, typename U, typename... Mixins>
struct get_index<T, N, U, Mixins...>
{
static constexpr int value = get_index<T, N + 1, Mixins...>::value;
};
class ComponentBase {
public:
virtual void update () = 0;
};
template <class T>
class Component : public ComponentBase {
public:
virtual void update () {
static_cast<T*>(this)->update();
}
};
class TransformComponent : public Component<TransformComponent> {
public:
void update () {
std::cout << "TransformComponent" << std::endl;
}
protected:
int _x, _y;
};
class ColliderComponent : public Component<ColliderComponent> {
public:
void update () {
std::cout << "ColliderComponent" << std::endl;
}
protected:
bool _collided;
};
template <typename... Mixins>
class GameObject
{
public:
GameObject(Mixins&... mixins) : _components{&mixins...} {}
GameObject(Mixins&&... mixins) : _components{&mixins...} {}
constexpr size_t getNumComponents () const { return _numComponents; };
template <typename T>
constexpr int getIndex() const {
return get_index<T, 0, Mixins...>::value;
}
template <typename T>
constexpr T* getComponent() const {
if (getIndex<T>() != -1)
return static_cast<T*>(_components[getIndex<T>()]);
else
return nullptr;
}
void update () {
for (size_t i = 0; i < getNumComponents(); ++i) {
_components[i]->update();
}
}
protected:
static const size_t _numComponents = sizeof...(Mixins);
ComponentBase* _components[sizeof...(Mixins)];
};
int main()
{
ColliderComponent c{};
TransformComponent t{};
GameObject<ColliderComponent, TransformComponent> b{ c, t };
b.getComponent<ColliderComponent>()->update();
b.getComponent<TransformComponent>()->update();
}