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

2

6

I have created a physics system that handles any collision object to any collision object like so:

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

and there are several other helper objects to make it easy to use, but the gist is that there are dynamic objects that need to be tested against static objects and other dynamic objects, but static objects don't need to be checked.

What I would like is something like this:

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

I'd love to deduce the containers through the add functions so using the system automatically updates the type lists. I think I get how to generate a typelist with a template function, but not how to then get it where I need it, or at what point in compilation it is complete.

If not that then some system using two typelists that then internally writes the update function to iterate through all the lists pairing them up against each other.

I've read some of the boost MPL book and read Andrei's book several times. But, I seem to get caught up in the how it works stuff and don't really translate that into how do I use it. I wish they had one more section on real world examples in the MPL book.

I've been able to get all of the pieces of a game engine to interact with rendering, physics, collisions (I separate detection from reaction), input, network, sound, etc. All in generic ways. Now I just need to hold all the things in a generic way. After all that generic work, it would be silly to require inheritance just so I can hold something in a container and I don't want to hand code every collection possibility as that is one of the great benefits of generic programming.

I saw Jalf had indicated that s/he used MPL to do something similar, but did not go into details enough for me to figure it out. If anyone knows a practical use example or where I can get more info on using the MPL I'd be grateful.

Thanks again!

Update

boost MPL and boost Fusion both seem to do what I want, but there appears to be very little in the way of good real life examples of either libraries. The documentation for MPL is little more than this template does this and good luck understanding the implications of that. Fusion is a bit better with "Here's an example but it's just the tip of the iceberg!"

A typical boost MPL example is has_xxx. They use XXX and xxx in the example making it difficult to see the difference where XXX(The required text) and Test or CheckType or any more distinguishable user type could be used in place of xxx. Plus there is no mention that none of this is in a namespace. Now I know why Scott meyers compared this to the shower scene in Psycho.

It's a shame really because what little I have gotten to compile and understand does really useful things, but is so hard to figure out I would never spend this much effort if I was on a shipping product.

If anyone knows real world examples or better references, explanations, or tutorial I would be grateful.

Update

Here's more code:

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

Then to use it I do this

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

Then all I need is to call collide with all my active collidable objects against all my active and passive objects.

I'm not using std::function because the addition of function names makes the code clearer to me. But maybe that's just legacy thinking.

Something answered 26/4, 2011 at 7:16 Comment(8)
mpl containers have no runtime data, use fusion containers insteadWaldon
Thanks, I'm looking into them.Something
Also, I think the pattern you are after is multiple dispatch. So you can re-re-read the related chapter from Andrei's book ... CheersSenescent
@Senescent I'll look at that chapter again. I haven't read it in some time. I have the double dispatch done. What I don't have is an automagic collision world container that knows what sequences are inside and which ones are dynamic and which are static.Something
@Tavison: perhaps I just don't fully understand :) I've +1 the question regardlessSenescent
@Senescent I might be looking at something like the brute force dispatcher. I'll re-read the chapter again. I'd love the typelist to be generated automatically, and maybe adding an extra object will do that.Something
@Senescent Oh, and I have everything from collision on down working fine. I just have to manually write the code for every combination of containers of types and that's what I'm trying to avoid.Something
@Tavsion: IIRC I'm pretty sure Alexandrescu had a solution to that point, but I don't have my bookshelf handySenescent
S
1

This is not complete And I did not get everything I want, but it's good enough for now. I'm entering the whole solution in case it helps others.

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

This isn't the final object I need, but now I have all the pieces to make what I want.

Update

And finally I get this.

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

Here TL is a boost::mpl container of what types should be held.

I think that provides a good starting point for expanding, but covers much of the metaprogramming parts.

I hope this helps others.

Something answered 2/5, 2011 at 11:9 Comment(0)
Q
3

If I understand correctly your problem is:

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

where some_type1 and some_type2 are unrelated and you're unwilling to redesign them to use dynamic polymorphism.

I don't think either MPL or Fusion will do what you want with this form. If your problem is what container to use as a member of PhysicsWorld, then no amount of compile-time computations will help: the member type is determined at instantiation time, i.e. the line manager m;.

You could rewrite the manager in a somewhat meta-programing fashion to use it this way:

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);

This is indeed the sort of things MPL+Fusion can help with. However this still remains quite anchored in the compile-time world: can you imagine writing an template<typename Iter> void insert(Iter first, Iter last) just so you can copy the contents of a container into a manager?

Allow me to assume that your requirements are such that in fact the manager has to be used in a much more runtimey fashion, like in my original formulation of your question. (I don't think that's quite a stretch of the imagination for a PhysicsWorld). There is an alternative, which I think is more appropriate, much less verbose and more maintanable: type-erasure. (The name of the technique may be a bit unfortunate and can be misleading the first time.)

A good example of type-erasure is 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 is a technique to bridge compile-time with runtime: in both assignments above, the arguments are unrelated types (one of which is non-class so not even remotely runtime polymorphic), but std::function handles both, provided they fulfill the contract that they can be used as f() (where f is an instance of the respective type) and that the expression has type (convertible to) void. The contract here is the compile-time aspect of type-erasure.

I'm not going to demonstrate how to implement type-erasure because there is a great Boostcon 2010 presentation on the subject. (You can watch the presentation and/or get the slides through the link). Or I (or someone else) can do it in the comments.

As a final note, implementation of type-erasure (typically) uses dynamic polymorphism. I mention that because I noticed you considered the use of typelists as a runtime object stored as a manager member. This smells like poor man's reflection, and really, poor man's dynamic polymorphism. So don't do that please. If you meant typelists as in the result of a MPL computation then disregard the node.

Quinta answered 26/4, 2011 at 23:49 Comment(5)
Final note first, yes I am looking for a compile time computation of the type list. And I am using function extensively, but even though you can marry non related types together this way you cannot store them together. If we imagine a completely generic world where generic programming took hold instead of OOP then the idea of a container that held objects that agreed to a contract, but were not related, would not seem so strange. The containment problem is the final problem and I think the tip to re-read modern C++ chapter 11 is what I had missed.Something
and thanks for the link. Any topics on this are very helpful. I may seem like a nut case, but deciding in advance this would be purely generic has helped me learn many cool things, but more important is I have almost finished a complete game engine design with surprising and unexpected benefits. It's very easy to use correctly and hard to use incorrectly, always nice, but it's surprisingly easy to extend. Far more than I expected.Something
@Something an std::vector<std::function<void()>> can indeed store together different, unrelated types.Quinta
Yes but the signature has to match so if one object's get collidable() returns a sphere it cannot be stored with one that stores a box or a mesh. At some point the objects need to be known again for the final collision test. Or is there something I'm missing. I know that turning things upside down can fix things, but I don't see how to get the final concrete, concrete test this way. And I don't know how I get my type back again without something like bool doYouHaveABox(). I'll add more of my current code so maybe you can show me what I've missed.Something
Watching the video, but it crashed so I'll download it. I'm starting to get the idea. From the level of the object up it has no type, but the type is not erased if I understand correctly. So I can still double dispatch based on types I just do it lower than the object. I think that would be perfect if it is what I gather so far.Something
S
1

This is not complete And I did not get everything I want, but it's good enough for now. I'm entering the whole solution in case it helps others.

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

This isn't the final object I need, but now I have all the pieces to make what I want.

Update

And finally I get this.

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

Here TL is a boost::mpl container of what types should be held.

I think that provides a good starting point for expanding, but covers much of the metaprogramming parts.

I hope this helps others.

Something answered 2/5, 2011 at 11:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.