0

クラスAがあり、デバイスドライバーと対話するときに共有ライブラリに配置する予定です。

私はクラスBを持っており、将来は共有ライブラリを使用してクラスAを使用するC、D、E...になる可能性があります。

クラスAにコールバック関数を設定して、特定のイベントが発生したときに、クラスB、C、D、E...の非静的メンバー関数がクラスAによって呼び出されるようにする機能が必要です。

GoogleでC++のコールバック関数を検索しましたが、非静的メンバー関数がCスタイルのコールバック定義でサポートされていないことがわかりました。

関数ポインタを使用して実行できますか?

OOPSの概念に違反しないC++のコールバックについていくつか提案してください。

同様の機能を提供する「Boost」というライブラリもありましたが、可能であれば、余分なライブラリのオーバーヘッドを避けたいと思います。コールバック関数にBoostをお勧めしますか?

編集: B、C、D、Eは階層を共有せず、完全に独立したクラスになります。ただし、それらはすべてクラスAのオブジェクトを持ちます。また、クラスAには、コールバック関数を設定するためのパブリック関数もあります。

4

2 に答える 2

4

ポリモーフィック関数ラッパーのほとんど重要でないオーバーヘッドを本当に避けたい場合の1つのオプションは、これらの関数を静的void*にし、関数がメンバーであるクラスの適切なインスタンスを指す「ユーザーデータ」パラメーターを受け取るようにすることです。の。次に、静的関数内で、適切なタイプにキャストバックします。

#include <iostream>

struct A{
  typedef void (*callback_type)(void*, int);
  callback_type callback;
  void* user_data;
  void set_callback(callback_type cb, void* ud){
     callback = cb; user_data = ud;
  }
  void invoke(){ callback(user_data, 42); }
};

struct B{
  static void cb_foo(void* vself, int data){
    B* self = static_cast<B*>(vself);
    self->foo(data);
  }
  void foo(int data){ std::cout << data * 2 << "\n"; }
};

struct C{
  static void cb_bar(void* vself, int data){
    C* self = static_cast<C*>(vself);
    self->bar(data);
  }
  void bar(int data){ std::cout << data / 2 << "\n"; }
};

int main(){
  A a;
  B b;
  a.set_callback(&B::cb_foo, &b);
  a.invoke();
  C c;
  a.set_callback(&C::cb_bar, &c);
  a.invoke();
}

Ideoneの実例。

std::functionただし、上記はコールバックとして受け入れることができるものが大幅に制限されているため、個人的には使用することをお勧めします。std::functionはポリモーフィック関数ラッパーです。つまり、通常の関数ポインター、メンバー関数ポインター、さらには関数(関数オブジェクト)を受け取り、それらすべてを同じ方法で呼び出すことができます。std::bindパラメータを関数にバインドできる、と一緒に、メンバー関数への簡単なコールバックを作成できます。Boostもそれらを提供します(Boost.FunctionBoost.Bind)。

#include <iostream>
#include <functional> // C++11
//#include <boost/function.hpp>
//#include <boost/bind.hpp>

struct A{
  std::function<void(int)> callback;
  void invoke(){ callback(42); }
};

struct B{
  void foo(int data){ std::cout << data * 2 << "\n"; }
};

struct C{
  void bar(int data){ std::cout << data / 2 << "\n"; }
};

int main(){
  using namespace std::placeholders; // namespace for argument placeholders for std::bind
                                     // not needed for Boost.Bind
  A a;
  B b;
  a.callback = std::bind(&B::foo, &b, _1);
  a.invoke();
  C c;
  a.callback = std::bind(&C::bar, &c, _1);
  a.invoke();
};

Ideoneの実例。

基本的std::bindに、最初のバージョンで手動で実行しなければならなかったことを自動的に実行し、オブジェクトポインターを保存して、その上でメンバー関数を呼び出します。void*ただし、これはポインターを介して行われるのではなく、std::bindオブジェクトポインターごとに異なるバインダータイプを返します。std::functionそれはあなたがそれを渡すものを気にしないので、あなたが必要とする理由です。

于 2012-07-17T12:59:55.247 に答える
0

{B、C、D、E}がいくつかの階層を共有しているため、クラスごとに新しいバージョンを作成する必要がないと仮定して、コールバック関数を静的にしますが、{B、C、Dへの参照である追加のパラメーターを追加します、E}コールバックに関与するインスタンス。そうすれば、関数のコンテキスト内に入ると、関連するオブジェクトに対して非静的関数/操作を呼び出すことができます。

{B、C、D、E}のクラス階層など、将来登場するものが同じでなく、共通の開始点が見つからない場合は、voidポインタなどのより一般的なものに頼る必要があります。ただし、オブジェクトでどの機能を呼び出すことができるかを知ることは非常に困難です。

于 2012-07-17T12:36:09.613 に答える