2010-04-27 61 views
1

我有一個C++庫,應該公開一些系統\資源調用作爲來自鏈接應用程序的回調。例如:接口應用程序(使用這個庫)可以發送套接字管理回調函數 - 發送,接收,打開,關閉等,庫將使用這個實現來代替庫的實現。 (這種方式可以讓應用程序自行管理套接字,這很有用)。 這個庫不得不公開更多的回調,比如密碼驗證,所以我想知道是否有一個首選方法在一個API中公開回調發送選項。 喜歡的東西:什麼是公開回調API的最佳方式 - C++

int AddCallbackFunc (int functionCallbackType, <generic function prototype>, <generic way to pass some additional arguments>) 

然後我的圖書館內,我將回調根據functionCallbackType參數分配給相應的函數指針。

有什麼辦法以通用的方式實現它,它將適合任何函數原型和任何額外的參數?

您的幫助將超過讚賞... 謝謝!

+0

接口怎麼樣?虛擬增變器爲回調設置「參數」和虛函數。 – 2010-04-27 15:55:03

回答

3

爲什麼不接受0參數functor並且只是讓用戶使用boost::bind在註冊之前構建參數?基本上,例如(調用,而不是商店,但你明白了吧):

#include <tr1/functional> 
#include <iostream> 

void callback(const std::tr1::function<int()> &f) { 
    f(); 
} 

int x() { 
    std::cout << "x" << std::endl; 
    return 0; 
} 

int y(int n) { 
    std::cout << "y = " << n << std::endl; 
    return 0; 
} 

int main(int argc, char *argv[]) { 
    callback(x); 
    callback(std::tr1::bind(y, 5)); 
} 

編輯:有一個選項B,這是基本實現與結構的引擎蓋下做存儲所有需要的綁定多態性的信息和繼承......它變得很混亂。我不會推薦它,但它會起作用。您也可以通過強制返回類型int來避免悲傷,但這隻會爲您節省一點。

#include <iostream> 

struct func_base { 
    virtual int operator()() = 0; 
}; 

// make one of these for each arity function you want to support (boost does this up to 50 for you :-P 
struct func0 : public func_base { 
    typedef int (*fptr_t)(); 

    func0(fptr_t f) : fptr(f) { 
    } 

    virtual int operator()() { return fptr(); } 

    fptr_t fptr; 
}; 

// demonstrates an arity of 1, templated so it can take any type of parameter 
template <class T1> 
struct func1 : public func_base { 
    typedef int (*fptr_t)(T1); 

    func1(fptr_t f, T1 a) : fptr(f), a1(a) { 
    } 

    virtual int operator()() { return fptr(a1); } 

    fptr_t fptr; 
    T1 a1; 
}; 

void callback(func_base *f) { 
    (*f)(); 
} 

int x() { 
    std::cout << "x" << std::endl; 
    return 0; 
} 

int y(int n) { 
    std::cout << "y = " << n << std::endl; 
    return 0; 
} 

int main(int argc, char *argv[]) { 
    // NOTE: memory leak here... 
    callback(new func0(x)); 
    callback(new func1<int>(y, 5)); 
} 
+0

謝謝 - 但我不想使用外部庫。 – rkellerm 2010-04-27 15:46:47

+1

不幸的是,這是通過一個很好的方式來做到這一點的最好方法,你總是可以使用tr1。基本上沒有實現手動綁定(大量的工作),我沒有看到一個簡單的方法來很好地做到這一點。 – 2010-04-27 15:47:18

0

聽起來像是您在尋找Functor。基本上每個回調類型都有一個類,其參數作爲數據成員和operator()來調用該功能。

+0

是的,剩下的唯一問題就是想出一種方法來存儲不同的函子(在'operator()中不使用任何參數',但是在一個集合中技術上是不同的類型,所以它們可以在後面統一調用。 boost/tr1的函數<>' – 2010-04-27 15:53:14

+0

當然,通常使用仿函數,你將擁有一個共同的基類,這將允許它們存儲在一個集合中 – marijne 2010-04-28 10:17:02

1

如果你不想去任何可用的C++選項; std :: tr1 :: function,函數,具有通用基類的多態性等,您可以改爲使用C方法。

客戶端將回調函數和指向其參數的指針作爲void *傳遞,然後當調用它時,回調會將void *轉換爲正確的類型。您需要在回調旁邊存儲void *,並且您需要非常小心地使用對象的生命週期。

int AddCallbackFunc (int type, int(*callback)(void*), void* callbackData) 
+0

並且'void *'的使用可能是爲什麼它被認爲是不安全的第一個地方 – 2010-04-27 16:14:56

+0

但是有沒有另一種「C」方式來做到這一點?似乎是唯一的方法。 – rkellerm 2010-04-28 14:51:14

1

這是可以做到,使用模板和類型擦除的組合。

這個想法是採取任何類型,並將其包裝到具有已知接口的對象中。

class CallbackBase 
{ 
public: 
    virtual ~CallbackBase(); 

    virtual void execute(); 
}; 

template <class T> 
class Callback: public CallbackBase 
{ 
public: 
    explicit Callback(T functor): mFunctor(functor) {} 

    void execute() { mFunctor(); } 

private: 
    T mFunctor; 
}; 

而現在,我們可以把它包裝:

template <class Function> 
int AddCallbackFunc (int functionCallbackType, Function f) 
{ 
    std::auto_ptr<CallbackBase> c(new Callback<Function>(f)); 

    // do something with `c`. 
} 

我把它留給你綁定的參數,沒有圖書館的方法是創建一個仿函數。

相關問題