如何在Mixin类中按类型返回组件?

时间:2014-09-04 16:06:16

标签: c++ c++11 mixins variadic-templates template-meta-programming

我一直在尝试使用可变参数模板来实现使用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很新,所以感谢任何帮助。

2 个答案:

答案 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>()方法,并返回指向适当组件的指针(基于简化的简单代码):

Live demo link.

#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();
}