2

我正在尝试对以下情况进行编码:我有一个基类,它提供了一个用于处理事件的框架。我正在尝试为此使用一组指向成员函数的指针。它如下:

class EH { // EventHandler
   virtual void something(); // just to make sure we get RTTI
public:
  typedef void (EH::*func_t)();
protected:
  func_t funcs_d[10];
protected:
  void register_handler(int event_num, func_t f) {
    funcs_d[event_num] = f;
  }
public:
  void handle_event(int event_num) {
    (this->*(funcs_d[event_num]))();
  }
};

然后用户应该从这个派生其他类并提供处理程序:

class DEH : public EH {
public:
  typedef void (DEH::*func_t)();
  void handle_event_5();
  DEH() {
     func_t f5 = &DEH::handle_event_5;
     register_handler(5, f5); // doesn't compile
     ........
  }
};

此代码无法编译,因为 DEH::func_t 无法转换为 EH::func_t。这对我来说很有意义。在我的情况下,转换是安全的,因为下面的对象this实际上是 DEH。所以我想要这样的东西:

void EH::DEH_handle_event_5_wrapper() {
  DEH *p = dynamic_cast<DEH *>(this);
  assert(p != NULL);
  p->handle_event_5();
}

然后代替

     func_t f5 = &DEH::handle_event_5;
     register_handler(5, f5); // doesn't compile

在 DEH::DEH() 放

     register_handler(5, &EH::DEH_handle_event_5_wrapper); 

所以,最后的问题(花了我足够长的时间......):有没有办法EH::DEH_handle_event_5_wrapper自动创建这些包装器(如)?或者做类似的事情?这种情况还有哪些其他解决方案?

谢谢。

4

3 回答 3

2

您可以简单地使用static_castto 转换DEH::func_tEH::func_t. 成员指针是逆变的:它们自然地向下转换层次结构,并且可以使用static_cast(与普通对象指针相反,它们是协变的)手动向上转换层次结构。

您正在处理的情况正是static_cast扩展功能以允许成员指针向上转换的原因。此外,成员函数指针的重要内部结构也以这种方式实现,以正确处理此类情况。

所以,你可以简单地做

DEH() {
   func_t f5 = &DEH::handle_event_5;
   register_handler(5, static_cast<EH::func_t>(f5));
   ........
}

我会说,在这种情况下,定义 typedef 名称是没有意义的DEH::func_t——它毫无用处。如果去掉定义DEH::func_t的典型注册码会如下所示

DEH() {
   func_t f5 = static_cast<func_t>(&DEH::handle_event_5); 
   // ... where `func_t` is the inherited `EH::func_t`
   register_handler(5, f5);
   ........
}

为了使它看起来更优雅,您可以为register_handlerin提供一个包装器DEH或使用其他方式(宏?模板?)来隐藏演员表。

此方法不为您提供任何方法来在调用时验证处理程序指针的有效性(就像您可以dynamic_cast在基于包装器的版本中所做的那样)。我不知道你有多关心这个检查。我会说,在这种情况下,它实际上是不必要和过度的。

于 2010-05-27T13:45:43.910 回答
0

你真的不应该这样做。查看 boost::bind

http://www.boost.org/doc/libs/1_43_0/libs/bind/bind.html

阐述

首先,我敦促您重新考虑您的设计。我见过的大多数事件处理程序系统都包含一个外部注册对象,该对象维护事件到处理程序对象的映射。您在 EventHandler 类中嵌入了注册,并且正在基于函数指针进行映射,这是不可取的。您遇到了问题,因为您正在围绕内置的虚函数行为结束运行。

等点boost::bind是从函数指针创建对象,允许您利用面向对象的语言特性。因此,boost::bind以您的设计为起点的实现将如下所示:

struct EventCallback
{
    virtual ~EventCallback() { }
    virtual void handleEvent() = 0;
};

template <class FuncObj>
struct EventCallbackFuncObj : public IEventCallback
{
    EventCallbackT(FuncObj funcObj) :
        m_funcObj(funcObj) { }
    virtual ~EventCallbackT() { }

    virtual void handleEvent()
    {
        m_funcObj();
    }

    private:
        FuncObj m_funcObj;
};

然后你的register_handler函数看起来像这样:

  void register_handler(int event_num, EventCallback* pCallback) 
  {
      m_callbacks[event_num] = pCallback;
  }

您的挂号电话会像:

register_handler(event, 
    new EventCallbackFuncObj(boost::bind(&DEH::DEH_handle_event_5_wrapper, this)));

现在,您可以从任何类型的(对象,成员函数)创建回调对象,并将其保存为给定事件的事件处理程序,而无需编写自定义函数包装对象。

于 2010-05-26T20:05:54.577 回答
0

为什么不直接使用虚函数?就像是

class EH {
public:
  void handle_event(int event_num) {

    // Do any pre-processing...

    // Invoke subclass hook
    subclass_handle_event( event_num );

    // Do any post-processing...
  }
private:
  virtual void subclass_handle_event( int event_num ) {}
};

class DEH : public EH {
public:
  DEH() { }
private:
  virtual void subclass_handle_event( int event_num ) {
     if ( event_num == 5 ) {
        // ...
     }
  }
};
于 2010-05-26T20:15:25.063 回答