2011-07-27 56 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()的參數傳遞給g()。任何想法我怎麼能做到這一點? –

+1

@John:我不明白你的問題。你需要縮小你想要做的事情,並提出一個真實的用例,而不是繼續提出假設。 – 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"?