我一直在尝试使用可变参数模板来实现使用Mixins的GameObject类(结合使用CRTP进行静态多态)。我有一切正常,但GameObject
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很新,所以感谢任何帮助。
答案 0 :(得分:3)
首先,我认为这里出了问题:
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
如您所见,我们不需要公共基类。 mixin支持所有提供update
方法的类型。当然,如果你需要基类,你可以修改它以获得基类。
答案 1 :(得分:2)
只需要一个类型的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();
}