我试图建立一个简单的事件调度系统。简单地说,我有所有派生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 *转换为它期望的派生类型;在这种情况下是MouseEvent *。 我怎样才能使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());
答案 0 :(得分:1)
我认为不可能将addListener()的函数参数更改为具有MouseEvent *参数(或从MouseEvent派生的参数)而不是IEvent *参数。
如果你有eventHandlers指向其他类(从IEvent派生)作为参数,那么你需要一个单独的addListener()函数,这可能不是你想要的。
简单地传递一个IEvent *,然后将它转换为eventHandler中的相应类型(正如你现在所做的那样)可能就是这样。
作为一个例子,Qt实际上为eventFilters做了类似的事情:http://qt-project.org/doc/qt-4.8/eventsandfilters.html
答案 1 :(得分:1)
浪费,因为它会在每个侦听器周围创建一个虚拟包装器。但是,我不知道是否可以干净地转换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 << "hello\n";
}
int
main ()
{
dispatcher x;
x.addListener <MouseEvent> (&callback);
}
有什么理由将event
作为指针传递而不是引用,btw?
答案 2 :(得分:1)
这可能不是您正在寻找的,但为什么不采取不同的方法。而不是存储函数,只需将指针(或smart_ptr
s)存储到具有handleEvent
函数的对象。像这样:
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)
。
答案 3 :(得分:0)
解决这个问题很容易。只需添加一个回调接口,例如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);
};