我正在尝试制作一个简单的事件调度器系统。简单地说,我有一些事件类,它们都派生了一个IEvent接口。我还有一个EventManager类,它有几个方法:
void addListener( int eventID, std::function<void( IEvent *event )> callback );
void dispatchEvent( int eventID, IEvent *event );
EventManager有一个矢量图:
std::map<int, std::vector<std::function<void( IEvent * )> > > m_events;
然后,任何对象都可以通过addListener方法注册,并给它一个回调,每当有人用相同的事件类型调用dispatchEvent时,就会通知它:
// add an event listener
EventManager::addListener(Event_MouseMove, std::bind(&Obj::callback, this, std::placeholders::_1));
// the callback method
void Obj::callback( IEvent *e )
{
// here I need to cast e to a MouseEvent *
// I want to get rid of this.
MouseEvent *me = (MouseEvent *)e;
}
调用dispatchEvent时,调用者必须传递一个IEvent派生类,MouseEvent例如:
// dispatching an Event
EventManager::dispatchEvent(Event_MouseMove, new MouseEvent());
我的问题与回调方法有关。假设你想监听MouseEvent,对象会有这样的方法:
void eventHandler( MouseEvent *event );
问题是回调无法传递给addListener方法,因为它需要IEvent*而不是MouseEvent*。为了使其工作,我只使用IEvent类型,然后事件处理程序方法必须将IEvent*强制转换为它所期望的派生类型;在本例中为鼠标事件*。如何使addListener方法将指针指向将接收任何IEvent派生类的方法?
例如:
// add an event listener
EventManager::addListener(Event_MouseMove, std::bind(&Obj::callback, this, std::placeholders::_1));
// the callback method
void Obj::callback( MouseEvent *e ) { ... }
// dispatching an Event
EventManager::dispatchEvent(Event_MouseMove, new MouseEvent());
如果您的eventHandlers将指向其他类(从IEvent派生)的指针作为参数,那么您需要为每个类单独使用addListener()函数,这可能不是您想要的。
简单地传递一个IEvent*,然后在eventHandler中将其强制转换为适当的类型(就像您目前正在做的那样)可能是一种方法。
例如,Qt实际上对其eventFilters做了类似的事情:http://qt-project.org/doc/qt-4.8/eventsandfilters.html
这有点浪费,因为它在每个侦听器周围创建了一个伪包装器。但是,我不知道是否可以干净地转换std::function
对象。如果在addListener()
中只接受函数指针(但随后排除了lambdas,meh),则可以直接转换。此外,一些reinterpret_cast
可能会有所帮助,但我觉得这并不容易,所以该解决方案没有一个功能。
#include <functional>
#include <iostream>
struct IEvent { };
struct MouseEvent : IEvent { };
template <typename type>
std::function <void (IEvent*)>
convert_callback (const std::function <void (type*)>& callback)
{
static_assert (std::is_convertible <type*, IEvent*>::value, "wrong type");
return [=] (IEvent* event) { return callback (static_cast <MouseEvent*> (event)); };
}
struct dispatcher
{
template <typename type>
void
addListener (std::function <void (type*)> callback)
{
std::function <void (IEvent*)> real_callback (convert_callback (callback));
MouseEvent event;
real_callback (&event);
}
};
void
callback (MouseEvent*)
{
std::cout << "hellon";
}
int
main ()
{
dispatcher x;
x.addListener <MouseEvent> (&callback);
}
有什么理由把event
作为指针而不是引用来传递吗?
这可能不是您想要的,但为什么不采取不同的方法呢。不存储函数,只存储指向具有handleEvent
函数的对象的指针(或smart_ptr
s)。类似这样的东西:
struct EventHander {
virtual ~EventHandler() {}
virtual void handle_event(IEvent *e) = 0;
};
struct Myhandler : public Eventhandler {
virtual void handle_event(IEvent *e) {
// Do whatever
}
};
因此,您只需存储EventHandler
指针,并在事件到达时为每个指针调用handler->handle_event(e)
。
解决这个问题很容易。只需添加一个回调接口,例如Callable
。请参阅下面的代码示例,该类充当Callable<void, Event*>
,并包装Event的任何子类。
template<typename Arg, /*traits here*/>
class FunctionWrapper : public Callable<void, Event*>
{
public:
FunctionWrapper(Callable<void, Arg*> *func)
{
mInternal = func;
}
FunctionWrapper(void (*func)(Arg*))
{
mInternal = new Function<void(Arg*)>(func);
}
template<typename T>
FunctionWrapper(T* obj, void (T::*func)(Arg*))
{
mInternal = new Function<void(T::*)(Arg*)>(obj, func);
}
void call(Event *e)
{
return mInternal->call(static_cast<Arg*>(e));
}
void operator ()(Event *e)
{
return call(e);
}
operator void*() const
{
return mInternal ? 0 : this;
}
operator bool() const
{
return mInternal != 0;
}
bool operator==(const FunctionWrapper& wrapper) const
{
return mInternal == wrapper.mInternal;
}
bool operator!=(const FunctionWrapper& wrapper) const
{
return mInternal != wrapper.mInternal;
}
virtual ~FunctionWrapper()
{
if(mInternal){
delete mInternal;
mInternal = 0;
}
}
private:
Callable<void, Arg*> *mInternal;
FunctionWrapper(const FunctionWrapper& wrapper);
const FunctionWrapper& operator=(const FunctionWrapper& wrapper);
};