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_castしてに変換するDEH::func_tことができますEH::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);
   ........
}

よりエレガントに見せるために、inのラッパーを提供するregister_handlerDEH、他の手段(マクロ?テンプレート?)を使用してキャストを非表示にすることができます。

このメソッドは、呼び出しの時点でハンドラーポインターの有効性を検証する手段を提供しません(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 に答える