9

オブジェクト指向のコールバックを c++ で簡単かつ便利に作成できるライブラリはありますか?

たとえば、言語 Eiffel には「エージェント」の概念があり、多かれ少なかれ次のように機能します。

class Foo{
public:
    Bar* bar;

    Foo(){
        bar = new Bar();
        bar->publisher.extend(agent say(?,"Hi from Foo!", ?));
        bar->invokeCallback();
    }

    say(string strA, string strB, int number){
        print(strA + " " + strB + " " + number.out);
    }

}

class Bar{
public:
    ActionSequence<string, int> publisher;

    Bar(){}

    invokeCallback(){
        publisher.call("Hi from Bar!", 3);
    }
}

出力は次のようになります: Hi from Bar! 3 フーからこんにちは!

そのため、エージェントは、メンバー関数をオブジェクトにカプセル化し、定義済みの呼び出しパラメーター (Hi from Foo) に沿って渡し、オープン パラメーター (?) を指定し、後で呼び出すことができる他のオブジェクトに渡すことができます。

C++ では非静的メンバー関数に関数ポインターを作成することは許可されていないため、C++ で簡単に使用できるものを実装するのはそれほど簡単ではないようです。C ++でのオブジェクト指向コールバックに関するGoogleの記事をいくつか見つけましたが、実際には、簡単にインポートできるいくつかのライブラリまたはヘッダーファイルを探しています。これにより、同様にエレガントな構文を使用できます。

誰でも私にいくつかのヒントがありますか?

ありがとう!

4

6 に答える 6

15

C ++でコールバックを使用する最もOOな方法は、インターフェイスの関数を呼び出してから、そのインターフェイスの実装を渡すことです。

#include <iostream>

class Interface
{
  public:
  virtual void callback() = 0;
};

class Impl : public Interface
{
  public:
  virtual void callback() { std::cout << "Hi from Impl\n"; }
};

class User
{
  public:
  User(Interface& newCallback) : myCallback(newCallback) { }

  void DoSomething() { myCallback.callback(); }

  private:
  Interface& myCallback;
};

int main()
{
  Impl cb;
  User user(cb);
  user.DoSomething();
}
于 2010-08-19T10:01:47.047 に答える
5

人々は通常、いくつかのパターンのいずれかを使用します。

継承。つまり、コールバックを含む抽象クラスを定義します。次に、それへのポインタ/参照を取得します。つまり、誰でもこのコールバックを継承して提供できます。

class Foo {
    virtual void MyCallback(...) = 0;
    virtual ~Foo();
};
class Base {
    std::auto_ptr<Foo> ptr;
    void something(...) {
        ptr->MyCallback(...);
    }
    Base& SetCallback(Foo* newfoo) { ptr = newfoo; return *this; }
    Foo* GetCallback() { return ptr; }
};

また継承。つまり、ルート クラスは抽象であり、ユーザーは具体的なクラスと専用のコールバック オブジェクトを持つのではなく、それを継承してコールバックを定義します。

class Foo {
    virtual void MyCallback(...) = 0;
    ...
};
class RealFoo : Foo {
    virtual void MyCallback(...) { ... }
};

さらに継承 - 静的。このように、テンプレートを使用してオブジェクトの動作を変更できます。これは 2 番目のオプションに似ていますが、実行時ではなくコンパイル時に機能するため、コンテキストに応じてさまざまなメリットとデメリットが生じる可能性があります。

template<typename T> class Foo {
    void MyCallback(...) {
        T::MyCallback(...);
    }
};
class RealFoo : Foo<RealFoo> {
    void MyCallback(...) {
        ...
    }
};

メンバー関数ポインターまたは通常の関数ポインターを取得して使用できます

class Foo {
    void (*callback)(...);
    void something(...) { callback(...); }
    Foo& SetCallback( void(*newcallback)(...) ) { callback = newcallback; return *this; }
    void (*)(...) GetCallback() { return callback; }
};

関数オブジェクトがあります-それらは operator() をオーバーロードします。現在 std::/boost:: 関数で提供されている機能的なラッパーを使用または作成する必要がありますが、ここでは単純なものも示します。これは最初の概念に似ていますが、実装を隠し、他のさまざまなソリューションを受け入れます。私は通常、これをコールバック メソッドとして使用しています。

class Foo {
    virtual ... Call(...) = 0;
    virtual ~Foo();
};
class Base {
    std::auto_ptr<Foo> callback;
    template<typename T> Base& SetCallback(T t) {
        struct NewFoo : Foo {
             T t;
             NewFoo(T newt) : t(newt) {}
             ... Call(...) { return t(...); }
        };
        callback = new NewFoo<T>(t);
        return this;
    }
    Foo* GetCallback() { return callback; }
    void dosomething() { callback->Call(...); }
};

適切なソリューションは、主にコンテキストによって異なります。C スタイルの API を公開する必要がある場合は、関数ポインターが唯一の方法です (ユーザー引数には void* を思い出してください)。実行時に変更する必要がある場合 (たとえば、プリコンパイル済みライブラリのコードを公開する場合)、ここでは静的継承を使用できません。

簡単なメモ: 私はそのコードを手作業で作成したため、完全ではなく (関数のアクセス修飾子など)、バグがいくつか含まれている可能性があります。これは例です。

于 2010-08-19T14:36:25.100 に答える
2

C ++では、メンバーオブジェクトへの関数ポインタを使用できます。
詳細については、こちらをご覧ください。boost.signalsまたはboost.signals2
を使用することもできます(プログラムがマルチスレッドであるかどうかに関係なく)。

于 2010-08-19T09:00:51.017 に答える
1

ファンクターのアイデアに参加する-std::tr1::functionとboost::bindを使用して、登録する前に引数を組み込みます。

于 2010-08-19T11:15:44.060 に答える
1

それを可能にするさまざまなライブラリがあります。boost::functionをチェックしてください。

または、独自の簡単な実装を試してください。

template <typename ClassType, typename Result>
class Functor
{
 typedef typename Result (ClassType::*FunctionType)();
 ClassType* obj;
 FunctionType fn;
public:
 Functor(ClassType& object, FunctionType method): obj(&object), fn(method) {}

 Result Invoke()
 {
  return (*obj.*fn)();
 }

 Result operator()()
 {
  return Invoke();
 }
};

使用法:

class A
{
 int value;
public:
 A(int v): value(v) {}

 int getValue() { return value; }
};


int main()
{
 A a(2);

 Functor<A, int> fn(a, &A::getValue);

 cout << fn();
}
于 2010-08-19T10:43:43.653 に答える
0

C++ には多くの可能性がありますが、一般的に問題は構文の 1 つです。

  • 状態が必要ない場合は関数へのポインターを使用できますが、構文は本当に恐ろしいものです。boost::bindこれは、さらに... 興味深い... 構文と組み合わせることができます (*)
  • 私はあなたの誤った仮定を修正します。メンバー関数へのポインターを持つことは実際に実行可能です。構文は非常に扱いにくく、逃げてしまいます (*)
  • Functor オブジェクトを使用できます。基本的に、Functor は()演算子をオーバーロードするオブジェクトです。たとえばvoid Functor::operator()(int a) const;、状態を持ち、共通のインターフェイスから派生する可能性があるオブジェクトであるためです。
  • オペレーターのオーバーロードの道に行きたくない場合は、コールバック関数にもっと適切な名前を付けて、独自の階層を簡単に作成できます。
  • 最後に、C++0x の機能を利用できます。std::function+ ラムダ関数は、表現力に関しては本当に素晴らしいです。

ラムダ構文のレビューをいただければ幸いです;)

Foo foo;
std::function<void(std::string const&,int)> func =
  [&foo](std::string const& s, int i) {
    return foo.say(s,"Hi from Foo",i);
  };

func("Hi from Bar", 2);
func("Hi from FooBar", 3);

もちろん、func実行可能な間だけ実行foo可能です (スコープの問題)。コピーfooを使用[=foo]して、参照渡しではなく値渡しを示すことができます。

(*)関数ポインタに関する必須のチュートリアル

于 2010-08-19T14:27:46.277 に答える