13

私は次のように定義された関数ポインタを持っています:

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++で記述できる、ある種の「カリー化」関数はありますか?

4

10 に答える 10

12

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

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 */ }
};
于 2008-09-30T01:51:27.603 に答える
11

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

boost::functionC++11をサポートしていないVisualStudio2008などの古いコンパイラを使用している場合に使用できます。
boost:functionそしてstd::function同じことです-彼らはC++11のstdライブラリにかなりのブーストのものを引き込みました。

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

于 2008-09-30T01:42:46.283 に答える
9

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

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

于 2008-09-30T04:29:56.427 に答える
4

MarshallClineによるC++FAQは、達成しようとしていることに役立つ場合があります。

于 2008-09-30T01:37:38.760 に答える
2

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

于 2008-09-30T01:40:53.667 に答える
2

C ライブラリとインターフェイスしている場合、 のようなものを使用せずにクラス メンバー関数を使用することはできませんboost::bind。通常、コールバック関数を受け取るほとんどの C ライブラリでは、選択した (通常は type の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
}
于 2008-09-30T04:17:27.440 に答える
1

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

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

...そして、Bのオブジェクトを使用してコールバックを呼び出すと、すべてが機能します。ただし、他のことを行う他のクラスで、Bの外部の関数を呼び出せるようにする必要があります。それは一種のコールバックのポイントです。しかし今、このタイプは間違いなくBの何かを指しています。より魅力的な解決策は次のとおりです。

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

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

于 2008-09-30T01:36:47.903 に答える
1

ブーストはオプションではないとおっしゃっていますが、TR1 はありますか?

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

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

于 2008-09-30T04:48:28.833 に答える
0

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

多分あなたが探しているのはメソッドへのポインタです。

于 2008-09-30T01:41:05.197 に答える
0

C++ フレームワークで使用する、まさにこのための一連のクラスがあります。

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

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

さまざまなパラメーターの組み合わせを処理するために、クラス タイプ 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);}
于 2008-09-30T04:04:54.927 に答える