2015-12-08 50 views
2

我正在为我的游戏引擎设计一个简单的事件系统。 我想要实现下列事件调度接口:C++中的简单事件系统

我想,我也会需要某种形式的内部调度的包装结构中,持有[pObject,pFunc]对。

struct Wrapper 
{ 
    A* pObj; // Of base class A. 
    // ?? // Pointer to given member function of A. 
}; 

我不知道如何实现成员函数指针逻辑。因为我需要一个实时系统,所以我想要一个相对较快的解决方案。我欣赏所有建议。

我宁愿避免第三方库。

+3

看看[std :: function](http://en.cppreference.com/w/cpp/utility/functional/function)。 –

+2

[This](http://blog.molecular-matters.com/2011/09/19/generic-type-safe-delegates-and-events-in-c/)也可以是一个好点,从中可以开始。 – skypjack

回答

3

根据意见,您可以使用std::function或与建议的here类似的结构。
它遵循的第二条建议的最小和工作示例:

class Dispatcher { 
    Dispatcher() { } 

    template<class C, void(C::*M)() = C::receive> 
    static void invoke(void *instance) { 
     (static_cast<C*>(instance)->*M)(); 
    } 

public: 
    template<class C, void(C::*M)() = &C::receive> 
    static Dispatcher create(C *instance) { 
     Dispatcher d; 
     d.fn = &invoke<C, M>; 
     d.instance = instance; 
     return d; 
    } 

    void operator()() { 
     (fn)(instance); 
    } 

private: 
    using Fn = void(*)(void *); 
    Fn fn; 
    void *instance; 
}; 

struct S { 
    void receive() { } 
}; 

int main() { 
    S s; 
    Dispatcher d = Dispatcher::create(&s); 
    d(); 
}; 

注意,它必须符合你的要求(目前,有针对性的成员方法没有返回值,并且不接受参数)相应的修改。
此外,它可以很容易地扩展为存储目标成员方法的数组,这就是您要查找的内容:只需存储一对instance s和invoke函数的向量。
另外,您可以使用Dispatcher函数的可变参数模板使其更加灵活。这样,您就可以定义具有不同返回类型和参数列表的Dispatcher

EDIT

它遵循一个Dispatcher接受多于一个的目标成员函数的一个例子。
如上所述,通过可变参数模板来扩展它仍然很容易。

#include <vector> 
#include <utility> 

class Dispatcher { 
    template<class C, void(C::*M)() = C::receive> 
    static void invoke(void *instance) { 
     (static_cast<C*>(instance)->*M)(); 
    } 

public: 
    template<class C, void(C::*M)() = &C::receive> 
    void bind(C *instance) { 
     auto pair = std::make_pair(&invoke<C, M>, instance); 
     targets.push_back(pair); 
    } 

    void operator()() { 
     for(auto pair: targets) { 
      (pair.first)(pair.second); 
     } 
    } 

private: 
    using Fn = void(*)(void *); 
    std::vector<std::pair<Fn, void*>> targets; 
}; 

struct S { 
    void receive() { } 
}; 

struct T { 
    void receive() { } 
}; 

int main() { 
    S s; 
    T t; 
    Dispatcher d; 
    d.bind(&s); 
    d.bind(&t); 
    d(); 
}; 

请注意,在上面的例子中,没有保证提交的instance指针仍然有效,一旦实际使用。您应该照顾绑定对象的生命周期,或者稍微修改示例以引入更安全的句柄。

2

如果你可以适当地构造你的A派生类,这样你就可以有一个process()方法,你可以简单地使用多态调度。

喜欢的东西:

struct A 
{ 
    virtual void process() = 0; 
    virtual ~A() {} 
} 
struct B : public A 
{ 
    virtual void process() { /* process event B... */ } 
} 
struct C : public A 
{ 
    virtual void process() { /* process event C... */ } 
} 

您的调度员的触发方式只会有呼吁指针process()方法所有注册对象的循环,让多态性调用适当的方法。

+0

这个解决方案是第一个想到的,但它假设我们知道所有可能的事件。 我的API需要灵活性,为用户创建自己的调度员(他自己的事件)并轻松注册所有参与者提供了可能性。 – DannyX