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

Could someone help me create a variable container using Boost::MPL?

本文关键字:变量 一个 MPL Boost 帮助 创建      更新时间:2023-10-16

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

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

我想通过add函数推导出容器,以便使用系统自动更新类型列表。我想我知道如何用模板函数生成类型列表,但不知道如何在我需要它的地方得到它,或者在编译的什么时候它是完整的。

如果不是这样,则某些系统使用两个类型列表,然后在内部编写更新函数来遍历所有列表,将它们相互配对。

我读了一些boost MPL的书,也读了Andrei的书好几遍。但是,我似乎陷入了它是如何工作的东西,并没有真正将其转化为我如何使用它。我希望他们在MPL的书中有一个关于真实世界的例子的章节。

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

我看到jhalf已经表明他/她使用MPL做了类似的事情,但没有深入到足够的细节让我弄清楚。如果有人知道一个实际使用的例子,或者在哪里我可以得到更多关于使用MPL的信息,我将不胜感激。

再次感谢!

boost MPL和boost 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);

那么我所需要做的就是调用collision与所有活动的可碰撞对象碰撞所有活动的和被动的对象

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

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

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! */

类型擦除是一种连接编译时和运行时的技术:在上面的两个赋值中,参数都是不相关的类型(其中一个是非类的,因此甚至不是远程运行时多态的),但是std::function处理这两个类型,提供它们满足它们可以用作f()(其中f是各自类型的实例)并且表达式具有类型(可转换为)void的契约。这里的契约是类型擦除的编译时方面。

我不打算演示如何实现类型擦除,因为在2010年的Boostcon上有一个很好的关于这个主题的演讲。(你可以观看演示和/或通过链接获取幻灯片)。或者我(或其他人)可以在评论中做。

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

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

#include <boostmplvector.hpp>
#include <boostmplfold.hpp>
#include <boostmplfor_each.hpp>
#include <boostmplinherit.hpp>
#include <boostmplinherit_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容器,它包含了应该保存的类型。

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

相关文章: