3

私は自分のイベント システムの正常に使用できる実装を作成しようとしています。イベント タイプを識別するために、私は「タイプ パス」と呼んでいるものを用意しています。これは、階層を通じてイベント タイプへのパスを識別します。このようにして、たとえば、キーであるかどうかにかかわらず、すべての InputEvents を 1 か所で処理できます。プレス、マウス入力、その他何でも. ただし、厄介な問題は、イベントの種類にそれらの ID を与えることです. 私が最近行ったことは、各インスタンスが Event クラスの静的メンバー関数からリーフ ID を取得することによってこれを行うことです. , この機能を実行する以外は、単にインターフェイスとして機能します. ただし、各タイプを確実にする最も簡単な方法はこの構造内に 1 つの ID があるだけで、タイプ パス (葉の ID/ID を除く) と typeid().hash_code() に基づくマップを使用するようです。

具体的には、私が望んでいるのは、大量の情報を調べたり、ばかげた定型文をたくさん実行したりすることなく、イベントを簡単に追加できるシステムです。これを考慮して(そして、おそらく私が望んでいることに気付いていないことはありますか?)、

  1. この設計には明らかな欠陥がありますか?
  2. typeid() を使用するよりも良い方法はありますか? 私はそれについて少し読んだことがありますが、意見を求められている人に応じて、決して使用されるべきではないか、ほとんど使用されるべきではないと考えられているようです. 私がたださびているか愚かである可能性があるので、誰かが解決策を知っているかどうかを知りたい.どちらが深刻な問題になるかはわかりません)。

私が今持っているもののかなり単純な例:

#include <iostream>
#include <vector>
#include <typeinfo>
#include <map>

void spew(std::vector<unsigned int> vect) { for (unsigned int i=0;i<vect.size();++i) std::cout << vect.at(i) << ","; std::cout << std::endl; }

class Foo
{
public:
    Foo() {}
    virtual ~Foo() {}
    static unsigned int getSubtype(std::vector<unsigned int> typePath, Foo *evt)
    {
        static std::map<std::vector<unsigned int>, std::map<std::size_t, unsigned int> > typeMap;
        std::size_t typehash = typeid(*evt).hash_code();
        if (typeMap.find(typePath) == typeMap.end())
        {
            unsigned int val = typeMap[typePath].size();
            typeMap[typePath][typehash] = val;
            return val;
        }
        else
        {
            if (typeMap[typePath].find(typehash) == typeMap[typePath].end())
            {
                unsigned int val = typeMap[typePath].size();
                typeMap[typePath][typehash] = val;
                return val;
            }
            return typeMap[typePath][typehash];
        }
    }
    virtual void test() { std::cout << "Foo" << std::endl; }
protected:
    std::vector<unsigned int> m_typePath;
};

class Bar : public Foo
{
public:
    Bar()
    {
        m_typePath.push_back(Foo::getSubtype(m_typePath, this));
        test();
    }
    virtual ~Bar() {}
    virtual void test() { std::cout << "Bar: "; spew(m_typePath);}
};

class Baz : public Foo
{
public:
    Baz()
    {
        m_typePath.push_back(Foo::getSubtype(m_typePath, this));
        test();
    }
    virtual ~Baz() {}
    virtual void test() { std::cout << "Baz: "; spew(m_typePath);}
};

class Qux : public Baz
{
public:
    Qux()
    {
        m_typePath.push_back(Foo::getSubtype(m_typePath, this));
        test();
    }
    virtual ~Qux() {}
    virtual void test() { std::cout << "Qux: "; spew(m_typePath);}
};

int main()
{
    Foo foo0;
std::cout << "----" << std::endl;
    Bar bar0;
std::cout << "----" << std::endl;
    Baz baz0;
std::cout << "----" << std::endl;
    Qux qux0;
}

出力:

----
Bar: 0,
----
Baz: 1,
----
Baz: 1,
Qux: 1,0,

明確にするために、このテストと他のテストは望ましい動作を示します。
編集: 前のタイトルは、私が質問したい内容と実際には一致しませんでした。関連する可能性のあるメモ: これはライブラリの一部を対象としており、高度に並列化されたものです。設計を簡単に表すために、同時実行に関連するコードは省略しましたが、そのような情報は設計の目的にも役立つ可能性があります。また、私は型識別子の作成/割り当てについてのみ支援を求めていることにも注意してください。これらについて言及するのは、暗黙の制約が与えられた場合、一部の設計が適用できない可能性があるためです。

Win編集:まあ、私はとてつもなく高速で、必要なことを正確に実行する実装を持っています。いくつかの派生クラスを使用すると、スレッドごとに 1,000 万をインスタンス化できます (他のテストのために TBB に追加しました。ただし、正確に 8 つのスレッドを使用する場合と使用しない場合があります)。そのパスは、通常 0.02 秒をはるかに下回ります。元の実装は、コンテナなどにもよりますが、約 4 ~ 5 秒しか管理できず、ばかげていました。結果(とにかく、アイデアを得るには十分です):

template<typename T> class EventID
{
public:
    static const std::size_t typeID;
};

template<typename T> const std::size_t EventID<T>::typeID = typeid(T).hash_code();

class Foo
{
public:
    Foo()
    {
        m_typePath.push_back(EventID<Foo>::typeID);
    }
protected:
    neolib::vecarray<std::size_t, 100, neolib::nocheck> m_typePath;
};

class Bar : public Foo
{
public:
    Bar()
    {
        m_typePath.push_back(EventID<Bar>::typeID);
    }
};
4

2 に答える 2

0

コンパイラが維持するクラス階層と、型コードのリストに依存します。

typedef enum { EVENT, INPUT, MOUSE, MOUSEDOWN, MOUSEUP, MOUSEMOVE, KEYBOARD, KEYDOWN, KEYUP } EventType;

typedef std::vector<EventType> ETVector;

class Event
{
public:
    virtual void appendType(ETVector& v) { v.push_back(EVENT); }
};

class InputEvent : public Event
{
public:
    virtual void appendType(ETVector& v) { v.push_back(INPUT); Event::appendType(v); }
};

class MouseEvent : public InputEvent
{
public:
    virtual void appendType(ETVector& v) { v.push_back(MOUSE); InputEvent::appendType(v); }
};

class MouseDownEvent : public MouseEvent
{
public:
    virtual void appendType(ETVector& v) { v.push_back(MOUSEDOWN); MouseEvent::appendType(v); }
};

class MouseUpEvent : public MouseEvent
{
public:
    virtual void appendType(ETVector& v) { v.push_back(MOUSEUP); MouseEvent::appendType(v); }
};

class MouseMoveEvent : public MouseEvent
// . . .

class KeyboardEvent : public InputEvent
// . . .

class KeyDownEvent : public KeyboardEvent
// . . .

class KeyUpEvent : public KeyboardEvent
// . . .

次に、テストを行うには、次のようになります。

KeyUpEvent kue;

EventTypeVector type_path;

kue.appendType(type_path);

for (EventTypeVector::const_iterator i = type_path.begin(); i != type_path.end(); i++)
{
    cout << *i << endl;
}

ベクターは型パスを格納します。実行時のストレージ コストはなく、static変数もありません。手動で管理された列挙型の代わりに使用できる可能性がありますtypeidが、列挙型を使用すると、すべてを制御できるため、プログラム内の他の型との競合を簡単に回避できます。または、テンプレートを使用してクラスにメソッドを吹き込みappendType()、コードをさらに削減することもできます。

で親クラスに明示的に名前を付ける必要があるのは少し面倒ですが、appendType()多重継承のおかげで、C++ で他の方法を知りません。Java では を使用することもできましたがsuper、リフレクションの方がおそらくより適切なアプローチでした。

これはあなたが持っているものよりも簡単ですか、それとも何か不足していますか? 幸運を。

于 2012-09-25T20:02:50.813 に答える