C++回调系统

C++ callback system

本文关键字:系统 回调 C++      更新时间:2023-10-16

我正在尝试制作一个简单的事件调度器系统。简单地说,我有一些事件类,它们都派生了一个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());
如果不将addListener()的函数参数更改为MouseEvent*参数(或从MouseEvent派生的参数)而不是IEvent*参数,我认为这是不可能的。

如果您的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_ptrs)。类似这样的东西:

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);
};