2011-07-27 23 views
2

たとえば、次の擬似コードでは、クラスBはB :: m_cbメンバを介してA :: Action()を呼び出す必要があります。

目的は、テンプレートではない一般的なコールバッククラスを作成する方法です。したがって、 "B"はテンプレートでなくてもよく、 "コールバック"はあらゆる種類の関数シグネチャを保持できます。

これまで私はこのようなコードを使用していましたが、今はその実装が見つかりません。 「コールバック」自体はテンプレートではありませんが、メンバーテンプレート
が含まれています - -
ヘルパー関数テンプレートmake_callbackは
C++コールバック - コールバック型をデカップリングする方法

は、誰もがpoiinterを与えるできるコールバックオブジェクトをインスタンス化します:私が覚えているすべてはありますか?

Class A 
{ 
public: 
    void Action(){//...}; 
}; 

class CallBack 
{ 
    //... 
    // CallBack it self it is a NOT a template 
    // It can wrap member template though 
}; 

class B 
{ 
public: 
    void SetCallback(CallBack to){ 
     m_cb = to; 
    } 
    void do_something() 
    { 
     //... 
     m_cb.Execute(); 
     //... 
    } 

private: 
    CallBack m_cb; 

}; 

int main() 
{ 
    A obj1; 
    CallBack cb = make_callback(&obj1, &A::Action); 
    B obj2; 
    obj2.SetCallback(cb); 
    //.... 
    obj2.do_something(); 
} 

私はこの同じウェブサイトから得たサンプルコードです。 私はそれを少し改良したので、任意のコールバック関数の戻り値の型を許容することができます。しかし、18行目のように任意の数の引数を扱うことはできません。また、Tはメンバ関数へのポインタです。これはCに依存する必要があります。これを強制する方法はわかりません。

#include <iostream> 
#include <memory> 

// INTERNAL CLASSES 

class CallbackSpecBase 
{ 
    public: 
    virtual ~CallbackSpecBase() {} 
    virtual void operator()(...) const = 0; 
}; 

template<class C, class T> 
class CallbackSpec : public CallbackSpecBase 
{ 
    public: 
    CallbackSpec(C& o, T m) : obj(o), method(m) {} 
/*line 18*/ void operator()(...) const { (&obj->*method)(); } // how to pass "..." into method(...) 

    private: 
    C& obj; 
    T method; 
}; 

// PUBLIC API 

class Callback 
{ 
    public: 
    Callback() {} 

    void operator()() { (*spec)(); } 

    template<class C, class T> 
     void set(C& o, T m) { spec.reset(new CallbackSpec<C, T>(o, m)); } 

    private: 
    std::auto_ptr<CallbackSpecBase> spec; 
}; 

// TEST CODE 

class Test 
{ 
    public: 
    void foo() { std::cout << "Working" << std::endl; } 
    void bar() { std::cout << "Like a charm" << std::endl; } 
}; 

int main() 
{ 
    Test t; 
    Callback c; 
    c.set(t, &Test::foo); 
    c(); 
    c.set(t, &Test::bar); 
    c(); 
} 
+1

ヒント:[Boost.Bind(http://www.boost.org/doc/libs/1_47_0 /libs/bind/bind.html)はすでにこの種の機能を提供していますので、自分で実装する必要はありません。 –

+0

Boost.Bindは任意のコールバック関数の署名を処理できますか?任意の型のように、引数の任意の数? –

答えて

1

コールバックはテンプレートである必要がありますので、あなたが(私の意見では完全にクレイジーなアイデアである)void*てパラメータを渡したい場合を除きこれは本当に、あなたの質問に答えていません。

私は同様の質問質問:完全な溶液が得られた回答のWhat is wrong with this variadic templates example?

ワン:

#include <memory> 

template< typename R, typename ... Args > 
class CallbackBase 
{ 
public: 
    typedef std::shared_ptr< CallbackBase< R, Args... > > 
      CallbackPtr; 

    virtual ~CallbackBase() 
    { 
    } 
    virtual R Call( Args ... args) = 0; 
}; 

template< typename R, typename ... Args > 
class FunctionCallback : public CallbackBase< R, Args... > 
{ 
public: 
    typedef R (*funccb)(Args...); 

    FunctionCallback(funccb cb_) : 
     CallbackBase< R, Args... >(), 
     cb(cb_) 
    { 
    } 
    virtual ~FunctionCallback() 
    { 
    } 
    virtual R Call(Args... args) 
    { 
     return cb(args...); 
    } 
private: 
    funccb cb; 
}; 

template < typename R, typename ...Args > 
typename CallbackBase< R, Args... >::CallbackPtr 
    MakeCallback(R (*cb)(Args...) ) 
{ 
    typename CallbackBase< R, Args... >::CallbackPtr 
     p(new FunctionCallback< R, Args... >(cb) 
); 
    return p; 
} 

bool Foo_1args(const int & t) 
{ 
    return true; 
} 
int main() 
{ 
    auto cbObj = MakeCallback(& Foo_1args); 
} 

はそれがお役に立てば幸いです。テンプレートが嫌な人は、いつでもtypedefを使うことができます。

+0

ありがとうございます。似たような別のサンプルコードが見つかりましたが、より良いと思います。パブリックAPIにはテンプレートがありません。私がまだ欠けているのは、コールバック関数の可変長引数リストを扱う方法です。このサンプルコードをこの質問に対する別の回答として投稿します。 –

+0

を確認してください8時間以内に私自身の質問に答えることはできません。私は私の質問の最後にコードを貼り付けました。チェックしてください。 –

2

あなたが探しているものはstd::function(C++ 0x)/ boost::functionです。これらは型の消去を使用してファンクションをファーストクラスのオブジェクトのように動作させます。私はこの記事に基づいてコールバック機構を実装している

class A 
{ 
public: 
    void Action() {//...}; 
}; 

class B 
{ 
public: 
    template <typename Func> 
    void SetCallback(Func func) { 
     m_cb = func; 
    } 

    void do_something() { 
     m_cb(); // whatever function 
    } 

private: 
    std::function<void()> m_cb; // anything callable with no arguments 
}; 

int main() 
{ 
    A obj1; 
    B obj2; 

    obj2.SetCallback(make_callback(&obj1, &A::Action)); 
    // or: 
    obj2.SetCallback(std::bind(&obj1, &A::Action)); // or boost::bind 

    obj2.do_something(); 
} 
+0

お返事ありがとうございます。私はちょうど標準のC++でこれを行うことができるサンプルコードを発見しました。私がまだできないことは、任意の関数の署名に対処することです。 "..."を別の関数に渡す必要があるようです:void f(...){g(...);} // f()のargsをg()に渡します。どのように私はそれを行うことができます考えですか? –

+1

@ジョン:私はあなたの質問を理解していません。あなたは何をしようとしているのかを絞り込み、仮説を尋ねずに、実際のユースケースを考え出す必要があります。 – GManNickG

+0

@John Crane:署名はコンパイル時に修正する必要があります。バリデーショナルなテンプレートでも、コンパイル時に変えることができます。 C++は実行時のバリアント引数をサポートしません。静的に型を指定する必要があります。 – Puppy

2

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

あなたはグローバル関数静的メンバ関数を使用するか、その実装を使用すると、このような何かを行うことができますまたは非スタティックメンバ関数をコールバックとして使用します。

この記事では、これらのすべての型のファンクタとしてデリゲートオブジェクトを作成する方法について説明します。 class Bには、メンバーとしてのデリゲートオブジェクトと、コールバック関数を登録するメソッドと、それを呼び出すメソッドがあります。 class Bも、あなたのコールバック関数もテンプレート化する必要はありません!代理オブジェクトには、非静的メンバ関数がコールバックとして使用され、テンプレート化された静的ラッパー関数へのポインタが使用される場合のオブジェクトポインタが含まれます。このラッパー関数では、コールバック関数への関数ポインタが格納されます。これは直接渡されるか、渡されたオブジェクトポインタにバインドされる前に呼び出されます。

class B 
{ 
    public: 
    void SetCallback(CDelegate f_Delegate) 
    { 
     m_Delegate = f_Delegate; 
    } 
    static void do_something() 
    { 
     //... 
     m_Delegate(); 
     //... 
    } 

    private: 
    static CDelegate m_Delegate; 
}; 

更新:

機構はまた、ここに記載されている:

5 years later, is there something better than the "Fastest Possible C++ Delegates"?
関連する問題