3

我正在尝试对我的事件系统进行合理可用的实现。为了识别事件类型,我有我所谓的“类型路径”,它通过层次结构识别事件类型的路径。这样,例如,我可以在一个地方处理所有 InputEvents,无论它们是否是关键按下,鼠标输入或其他任何东西。但是,一个棘手的问题是为事件类型赋予其身份。我最近所做的是通过让每个实例从 Event 类的静态成员函数中检索叶身份来做到这一点, 它只是作为一个接口而不是执行这个功能. 但是, 确保每个类型的最简单方法在这个结构中只有一个身份似乎是使用基于类型路径(直到但不包括叶身份/标识符)和 typeid().hash_code() 的映射。

具体来说,我想要的是一个可以轻松添加事件的系统,而无需查找大量信息或执行大量愚蠢的样板废话。考虑到这一点(可能还有我没有意识到我应该想要的东西?),

  1. 这种设计是否有任何明显的缺陷?
  2. 有没有比使用 typeid() 更好的方法?我已经阅读了一些关于它的内容,它似乎被认为是某种东西,取决于被询问意见的人,要么永远不要使用,要么几乎永远不要使用。因为我可能只是生疏或愚蠢,我想知道是否有人知道一个解决方案,如果没有别的,那么不确定性(显然 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 编辑:嗯,我有一个非常快的实现,并且完全符合我的需要。使用一些派生类,我可以在派生类中为每个线程实例化一千万个(我在 TBB 中添加了一些其他测试;它可能会或可能不会使用八个线程,但它可以随意)分布在派生类中,每个线程都有两个或多个元素它的路径,通常在 0.02 秒以下。最初的实现根据容器等管理大约四到五秒,这很愚蠢。结果(无论如何,足以得到这个想法):

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 回答
0

请参阅对原始帖子的最终编辑以获取解决方案。

于 2013-02-24T02:43:01.603 回答