6

次のように、任意の衝突オブジェクトを任意の衝突オブジェクトに処理する物理システムを作成しました。

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 関数を使用してコンテナーを推測したいので、システムを使用するとタイプ リストが自動的に更新されます。テンプレート関数を使用してタイプリストを生成する方法はわかったと思いますが、必要な場所やコンパイルのどの時点でそれを取得するかはわかりません。

そうでない場合は、2 つのタイプリストを使用するシステムが更新関数を内部的に記述して、すべてのリストを反復処理し、それらを相互にペアリングします。

私はブースト MPL の本をいくつか読み、Andrei の本を数回読みました。しかし、私はそれがどのように機能するかに夢中になっているようで、実際にそれをどのように使用するかには変換していません. MPLの本に実世界の例に関するセクションがもう1つあればいいのにと思います。

ゲーム エンジンのすべての部分を、レンダリング、物理、衝突 (検出と反応を分けています)、入力、ネットワーク、サウンドなどと相互作用させることができました。すべて一般的な方法で行います。今、私はすべてのものを一般的な方法で保持する必要があります。すべての汎用的な作業の後、コンテナーに何かを保持できるようにするためだけに継承を要求するのはばかげています。コレクションのすべての可能性をコードに渡したくありません。これは汎用プログラミングの大きな利点の 1 つです。

Jalf は、MPL を使用して同様のことを行っていることを示していましたが、それを理解するのに十分なほど詳しく説明していませんでした。誰かが実際の使用例を知っているか、MPL の使用に関する詳細情報を入手できる場所を知っていれば、私は感謝します。

再度、感謝します!

アップデート

boost MPL と boost Fusion はどちらも私が望んでいるように見えますが、どちらのライブラリの実際の良い例もほとんどないようです。MPL のドキュメントは、このテンプレートがこれを行う以上のものではありません。その意味を理解してください。フュージョンは、「例を示しますが、これは氷山の一角にすぎません!」の方が少し優れています。

ブースト 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 は使用していません。しかし、それは単なるレガシー思考なのかもしれません。

4

2 に答える 2

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あなたの要件は、実際には、あなたの質問の最初の定式化のように、はるかに実行時の方法で使用する必要があると仮定してください。(私はそれが a の想像力をかなり伸ばしているとは思いません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! */

型消去は、コンパイル時とランタイムを橋渡しする手法です。上記の両方の割り当てでは、引数は無関係な型です (そのうちの 1 つは非クラスであるため、リモートでランタイム ポリモーフィックでさえありません) が、std::function は両方を処理します。それらが (f はそれぞれの型のインスタンスである)として使用できf()、式の型が (convertible to) であるという契約を満たしますvoid。ここでの契約は、型消去のコンパイル時の側面です。

タイプ消去の実装方法については説明しません。これについては、Boostcon 2010 の素晴らしいプレゼンテーションがあるためです。(プレゼンテーションを視聴したり、リンクからスライドを取得したりできます)。または、私 (または他の誰か) がコメントでそれを行うことができます。

最後に、型消去の実装では (通常) 動的ポリモーフィズムが使用されます。タイプリストをメンバーとして格納されたランタイム オブジェクトとして使用することを考慮していることに気付いたので、それについて言及しますmanager。これは貧乏人の反省の匂いがするし、本当に貧乏人の動的ポリモーフィズムの匂いがする。だから、そうしないでください。MPL 計算の結果としてタイプリストを意味する場合は、ノードを無視してください。

于 2011-04-26T23:49:18.503 に答える
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 コンテナーです。

これは拡張するための良い出発点を提供すると思いますが、メタプログラミング部分の多くをカバーしています。

これが他の人に役立つことを願っています。

于 2011-05-02T11:09:26.637 に答える