特定のオブジェクトインスタンスでC ++関数ポインターを呼び出す

StackOverflow https://stackoverflow.com/questions/151418

  •  02-07-2019
  •  | 
  •  

質問

次によって定義された関数ポインタがあります

typedef void (*EventFunction)(int nEvent);

C ++オブジェクトの特定のインスタンスでその関数を処理する方法はありますか?

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne() { handler(1); }
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(EventFromA); }  // What do I do here?

    void EventFromA(int nEvent) { // do stuff }
};

編集: Orionは、Boostが提供する次のようなオプションを指摘しました:

boost::function<int (int)> f;
X x;
f = std::bind1st(
      std::mem_fun(&X::foo), &x);
f(5); // Call x.foo(5)

残念ながら、ブーストは私にとって選択肢ではありません。ある種の「カレー」がありますか?このような種類のメンバー関数へのポインターを通常の関数ポインターにラップするC ++で記述できる関数ですか?

役に立ちましたか?

解決

Don Clugstonの優れたFastDelegateライブラリを強くお勧めします。実際のデリゲートに期待するすべてのものを提供し、ほとんどの場合、いくつかのASM命令にコンパイルします。付随する記事は、メンバー関数ポインターについてもよく読んでください。

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

他のヒント

関数ポインタを使用して、特定のオブジェクトインスタンスのvtableにインデックスを付けることができます。これは、メンバー関数ポインターと呼ばれます。 &quot;。*&quot;を使用するには、構文を変更する必要があります。 &quot;&amp; ::&quot;演算子:

class A;
class B;
typedef void (B::*EventFunction)(int nEvent)

そして:

class A
{
private:
    EventFunction handler;

public:
    void SetEvent(EventFunction func) { handler = func; }

    void EventOne(B* delegate) { ((*delegate).*handler)(1); } // note: ".*"
};

class B
{
private:
    A a;
public:
    B() { a.SetEvent(&B::EventFromA); } // note: "&::"

    void EventFromA(int nEvent) { /* do stuff */ }
};

未処理のC ++関数ポインターから逃げ、 stdを使用します。 :function 代わりに。

boostを使用できます。 :: function C ++ 11をサポートしていないvisual studio 2008などの古いコンパイラを使用している場合。
boost:function std :: function は同じものです-C ++ 11のstdライブラリにかなりのブースト機能を追加しました。

注:わかりやすいので、Microsoftのドキュメントではなく、boost関数のドキュメントを読むことをお勧めします

Marshall ClineによるC ++ FAQ が役立つ場合があります。あなたが達成しようとしているものに。

メンバーへのポインターについて読む。 派生クラスのメソッドを呼び出すには、メソッドを仮想として基本クラスで宣言し、基本クラスでオーバーライドし、ポインターが基本クラスのメソッドを指すようにする必要があります。 仮想メンバーへのポインターの詳細。

Cライブラリとインターフェイスする場合、 boost :: bind などを使用せずにクラスメンバー関数を使用することはできません。コールバック関数を使用するほとんどのCライブラリでは、通常、選択した追加の引数(通常は void * 型)を渡すこともできます。これを使用して、クラスをブートストラップできます:


class C
{
public:
  int Method1(void) { return 3; }
  int Method2(void) { return x; }

  int x;
};

// This structure will hold a thunk to
struct CCallback
{
  C *obj;  // Instance to callback on
  int (C::*callback)(void);  // Class callback method, taking no arguments and returning int
};

int CBootstrapper(CCallback *pThunk)
{
  // Call the thunk
  return ((pThunk->obj) ->* (pThunk->callback))( /* args go here */ );
}

void DoIt(C *obj, int (C::*callback)(void))
{
  // foobar() is some C library function that takes a function which takes no arguments and returns int, and it also takes a void*, and we can't change it
  struct CCallback thunk = {obj, callback};
  foobar(&CBootstrapper, &thunk);
}

int main(void)
{
  C c;
  DoIt(&c, &C::Method1);  // Essentially calls foobar() with a callback of C::Method1 on c
  DoIt(&c, &C::Method2);  // Ditto for C::Method2
}

残念ながら、EventFunction型は正しい型ではないため、Bの関数を指すことはできません。あなたはそれを正しい型にすることができますが、おそらくそれはあなたが望む解決策ではないでしょう:

typedef void(* B :: EventFunction)(int nEvent);

Bそれがコールバックのポイントのようなものです。しかし、現在このタイプはBの何かを明確に指し示しています。より魅力的なソリューションは次のとおりです。

  • Bを基本クラスにしてから、呼び出される可能性のある他の各クラスの仮想関数をオーバーライドします。次に、Aは関数ポインターの代わりにBへのポインターを格納します。ずっときれい。
  • 関数を特定のクラス型、基本クラスにバインドしたくない場合(そして、私はあなたを責めないでしょう)、静的関数と呼ばれる関数を作成することをお勧めします:&quot; < code> static void EventFrom A(int nEvent); &quot;。その後、Bのオブジェクトを使用せずに直接呼び出すことができます。ただし、おそらくBの特定のインスタンスを呼び出す必要があります(Bがシングルトンでない場合)。
  • したがって、Bの特定のインスタンスを呼び出したいが、非Bのインスタンスも呼び出すことができるようにするには、コールバック関数が適切なオブジェクトを呼び出すことができるように、コールバック関数に何か他のものを渡す必要があります。上記のように関数を静的にし、Bへのポインターを作成するvoid *パラメーターを追加します。

実際には、この問題に対する2つの解決策があります。void*とイベントを渡すアドホックシステム、およびウィンドウシステムのような基本クラスの仮想関数を持つ階層

ブーストはオプションではないということですが、TR1は利用可能ですか?

TR1は、boostライブラリに基づいて、function、bind、およびmem_fnオブジェクトを提供します。すでにコンパイラにバンドルされている場合があります。まだ標準ではありませんが、最近使用したコンパイラが少なくとも2つありました。

http://en.wikipedia.org/wiki/Technical_Report_1
http://msdn.microsoft.com/en-us/library/bb982702 aspx

ここで何を成し遂げようとしているのかはやや不明確です。明らかなのは、関数ポインタが道ではないということです。

おそらくあなたが探しているのはメソッドへのポインタです。

c ++フレームワークで使用するこの正確なことのための一連のクラスがあります。

http://code.google.com/p /kgui/source/browse/trunk/kgui.h

どのように処理するかは、コールバックとして使用できるクラス関数ごとに、オブジェクト型をバインドする静的関数が必要です。自動的にそれを行う一連のマクロがあります。 &quot; CB_&quot;を除き、同じ名前の静的関数を作成します。プレフィックスとクラスオブジェクトポインターである追加の最初のパラメーター。

さまざまなパラメータの組み合わせを処理するために、クラスタイプkGUICallBackおよびそのさまざまなテンプレートバージョンをチェックアウトします。

#define CALLBACKGLUE(classname , func) static void CB_ ## func(void *obj) {static_cast< classname *>(obj)->func();}
#define CALLBACKGLUEPTR(classname , func, type) static void CB_ ## func(void *obj,type *name) {static_cast< classname *>(obj)->func(name);}
#define CALLBACKGLUEPTRPTR(classname , func, type,type2) static void CB_ ## func(void *obj,type *name,type2 *name2) {static_cast< classname *>(obj)->func(name,name2);}
#define CALLBACKGLUEPTRPTRPTR(classname , func, type,type2,type3) static void CB_ ## func(void *obj,type *name,type2 *name2,type3 *name3) {static_cast< classname *>(obj)->func(name,name2,name3);}
#define CALLBACKGLUEVAL(classname , func, type) static void CB_ ## func(void *obj,type val) {static_cast< classname *>(obj)->func(val);}
ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top