C ++回调系统

时间:2012-03-24 20:55:43

标签: c++

我试图建立一个简单的事件调度系统。简单地说,我有所有派生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());

4 个答案:

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