有人可以帮助我使用Boost :: MPL创建一个变量容器吗?

时间:2011-04-26 07:16:26

标签: c++ templates metaprogramming boost-mpl

我创建了一个物理系统,可以处理碰撞对象到任何碰撞对象,如下所示:

namespace Collision
{
    template <typename T, typename U>
    inline void Check(T& t, U& u)
    {
        if(u.CheckCollision(t.GetCollider()))
        {
            u.HitBy(t);
            t.Hit(u);
        }
    }
}

并且还有其他一些辅助对象使其易于使用,但要点是有动态对象需要针对静态对象和其他动态对象进行测试,但不需要检查静态对象。

我想要的是这样的:

void func()
{
    PhysicsWorld world;
    shared_ptr<CSphere> ballPhysics(new CSphere(0,0,ballSprite->Width()));
    BallCommand ballBehavior;
    CBounds bounds(0, 0, 640, 480);
    CBox obstacle(200, 150, 10, 10);

    Collision::Collidable<CBounds> boundC(bounds);
    Collision::Collidable<std::shared_ptr<CSphere>, BallCommand&> ballC(ballPhysics, ballBehavior);
    Collision::Collidable<CBox> obstC(obstacle);

    world.addStatic(boundC);
    world.addDynamic(ballC);
    world.addStatic(obstC);
    ...
    ...
    world.Update();
    ...
    ...
}

我想通过添加功能来推断容器,因此使用系统会自动更新类型列表。我想我得到了如何使用模板函数生成类型列表,但不知道如何在需要它的地方生成它,或者在编译的哪一点完成它。

如果不是那么一些系统使用两个类型列表然后在内部写入更新函数来迭代所有列表将它们相互配对。

我已经阅读了一些关于MPL的推文并多次阅读安德烈的书。但是,我似乎陷入了它的工作原理,并没有真正将其转化为我如何使用它。我希望他们在MPL书中再有一节关于真实世界的例子。

我已经能够将游戏引擎的所有部分与渲染,物理,碰撞(我将检测与反应分开),输入,网络,声音等进行交互。所有这些都是通用的方式。现在我只需要以通用方式保存所有内容。在完成所有这些通用工作之后,要求继承只是因为我可以在容器中保存一些东西并且我不想手动编写每个集合的可能性,因为这是泛型编程的一大好处。这将是愚蠢的。

我看到Jalf表示他/她使用MPL做类似的事情,但没有详细说明我能弄明白。如果有人知道实际使用示例或我可以获得有关使用MPL的更多信息,我将不胜感激。

再次感谢!

更新

提升MPL和提升Fusion似乎都做了我想要的事情,但是对于任何一个库的良好现实生活示例的方式似乎都很少。 MPL的文档只是这个模板做到这一点,并且好运了解它的含义。融合更好一点“这是一个例子,但它只是冰山一角!”

典型的boost MPL示例是has_xxx。他们在示例中使用XXX和xxx,这使得很难看到XXX(所需文本)和Test或CheckType或更可区分的用户类型可用于代替xxx的区别。另外,没有提到这些都不在命名空间中。现在我知道为什么斯科特迈耶斯将这与心理学中的淋浴场景进行了比较。

真的很遗憾,因为我编写和理解的东西确实很有用,但很难弄清楚,如果我在装运产品上,我将永远不会花费太多精力。

如果有人知道现实世界的例子或更好的参考,解释或教程,我将不胜感激。

更新

以下是更多代码:

template <typename T, typename V = VictimEffect, typename M = MenaceEffect>
class Collidable
{
    T m_Collider;
    V m_HitBy;
    M m_Hit;

public:
    Collidable(T collide, V victim, M menace) : m_Collider(collide), m_HitBy(victim),         m_Hit(menace) {;}
    Collidable(T collide) : m_Collider(collide) {;}
    Collidable(T collide, V victim) : m_Collider(collide), m_HitBy(victim) {;}

    T& GetCollider()
    {
        return m_Collider;
    }

    template <typename V>
    void HitBy(V& menace)
    {
        m_HitBy.HitBy(menace.GetCollider());
    }

    template <typename V>
    void Hit(V& victim)
    {
        m_Hit.Hit(victim.GetCollider());
    }

    template <typename V>
    bool CheckCollision(V& menace)
    {
        return m_Collider.CheckCollision(menace);
    }
};

然后使用它我这样做

    Collidable<Boundary, BallCommand> boundC(boundary, ballBehavior);
    Collidable<CollisionBox> ballC(circle);

然后我需要的是调用所有活动的可碰撞对象与我的所有主动和被动对象碰撞。

我没有使用std :: function,因为添加函数名会使代码更清晰。但也许这只是传统思维。

2 个答案:

答案 0 :(得分:3)

如果我理解正确你的问题是:

class manager {
public:
    template<typename T>
    void add(T t);

private:
    /* ??? */ data;
    /* other members? */
};

manager m;
some_type1 s1;
some_type2 s2;
m.add(s1);
m.add(s2);
/* m should hold its copies of s1 and s2 */

其中some_type1和some_type2不相关,你不愿意重新设计它们以使用动态多态。

我不认为MPL或Fusion会使用此表单执行您想要的操作。如果您的问题是要用作PhysicsWorld成员的容器,那么任何编译时计算都无济于事:成员类型是在实例化时确定的,即行manager m;

可以以某种元编程的方式重写管理器,以这种方式使用它:

typedef manager<> m0_type;
typedef typename result_of::add<m0_type, some_type1>::type m1_type;
typedef typename result_of::add<m1_type, some_type2>::type final_type;
/* compile-time computations are over: time to instantiate */
final_type m;
/* final_type::data could be a tuple<some_type1, some_type2> for instance */
m.add(s1); m.add(s2);

这确实是MPL + Fusion可以帮助的事情。然而,这仍然在编译时间世界中仍然存在:您能想象编写template<typename Iter> void insert(Iter first, Iter last)只是为了将容器的内容复制到管理器中吗?

请允许我假设您的要求实际上manager必须以更加运行时的方式使用,就像我原来的问题表达式一样。 (我认为这不是PhysicsWorld的想象力。有一种替代方案,我认为更合适,更简洁,更可维护:类型擦除。 (该技术的名称可能有点不幸,第一次可能会误导。)

类型擦除的一个很好的例子是std :: function:

std::function<void()> func;
func = &some_func; /* this just looks like that internally std::function stores a void(*)() */
func = some_type(); /* but here we're storing a some_type! */

Type-erasure是一种将编译时与运行时联系起来的技术:在上面的两个赋值中,参数都是不相关的类型(其中一个是非类的,所以甚至不是远程运行时多态),但std :: function同时处理,提供他们履行合同,可以将它们用作f()(其中f是相应类型的实例),并且表达式具有类型(可转换为)void 。这里的契约是类型擦除的编译时方面。

我不打算演示如何实现类型擦除,因为主题上有a great Boostcon 2010 presentation。 (您可以观看演示文稿和/或通过链接获取幻灯片)。或者我(或其他人)可以在评论中这样做。

作为最后一点,类型擦除(通常)的实现使用动态多态。我提到这一点,因为我注意到你考虑使用类型列表作为存储为manager成员的运行时对象。这闻起来像穷人的反思,真的是穷人的动态多态性。所以不要那样做。如果您在MPL计算结果中表示类型列表,则忽略该节点。

答案 1 :(得分:1)

这不完整而且我没有得到我想要的一切,但它现在已经足够好了。我正在进入整个解决方案以防其他人。

#include <boost\mpl\vector.hpp>
#include <boost\mpl\fold.hpp>
#include <boost\mpl\for_each.hpp>
#include <boost\mpl\inherit.hpp>
#include <boost\mpl\inherit_linearly.hpp>
#include <iostream>
#include <vector>

using namespace boost::mpl::placeholders;

typedef boost::mpl::vector<short, long, char, int> member_types;

template <typename T>
struct wrap
{
    std::vector<T> value;
};

typedef boost::mpl::inherit_linearly<member_types, boost::mpl::inherit<wrap<_2>, _1> >::type Generate;

class print
{
    Generate generated;

public:
    template <typename T>
    void operator()(T)
    {
        std::cout << *static_cast<wrap<T>&>(generated).value.begin() << std::endl;
    }

    template <typename T>
    void Add(T const& t)
    {
        static_cast<wrap<T>&>(generated).value.push_back(t);
    }
};

void main()
{
    print p;

    short s = 5;
    p.Add(s);
    long l = 555;
    p.Add(l);
    char c = 'c';
    p.Add(c);
    int i = 55;
    p.Add(i);

    boost::mpl::for_each<member_types>(p);
}

这不是我需要的最终目标,但现在我已经完成了我想要的所有部分。

<强>更新

最后我明白了。

template <typename TL>
class print
{
    template <typename T>
    struct wrap
    {
        std::vector<T> value;
    };

    typedef typename boost::mpl::inherit_linearly<TL, boost::mpl::inherit<wrap<_2>, _1> >::type Generate;
    Generate generated;

public:
    void Print()
    {
        boost::mpl::for_each<TL>(*this);
    }

    template <typename T>
    void operator()(T)
    {
        std::cout << *static_cast<wrap<T>&>(generated).value.begin() << std::endl;
    }

    template <typename T>
    void Add(T const& t)
    {
        static_cast<wrap<T>&>(generated).value.push_back(t);
    }
};

这里TL是一个boost :: mpl容器,应该保存什么类型。

我认为这为扩展提供了一个很好的起点,但涵盖了很多元编程部分。

我希望这有助于其他人。