2009-12-03 215 views
32

我想在一個C++應用程序使用C庫,並發現我在下列情況下(我知道我的C,但我是相當新的C++)的自我。在C方面,我有一個函數集合,它將函數指針作爲它們的參數。在C++方面,我有一個函子的對象,它具有與C函數所需的函數指針相同的簽名。有沒有辦法將C++函數作爲函數指針傳遞給C函數?傳遞仿函數的函數指針

回答

46

您不能直接將指針傳遞給C++函數對象,作爲指向C代碼 (甚至是C++代碼)的函數指針。

此外,爲了便於將回調傳遞給C代碼,至少需要將 聲明爲extern "C"非成員函數。至少,因爲一些API需要特定的函數調用約定,因此需要額外的聲明修飾符。

在許多環境中,C和C++具有相同的調用約定,並且在名稱修改中只有 ,因此任何全局函數或靜態成員都可以使用。 但是你仍然需要在正常的功能中打電話給operator()

  • 如果你的仿函數沒有狀態(這是一個對象,只是爲了滿足一些正規 要求等):

    class MyFunctor { 
        // no state 
    public: 
        MyFunctor(); 
        int operator()(SomeType &param) const; 
    } 
    

    你可以寫一個普通的extern「C」,它創造了仿函數功能執行它的 運算符()。

    extern "C" int MyFunctorInC(SomeType *param) 
    { 
        static MyFunctor my_functor; 
        return my_functor(*param); 
    } 
    
  • 如果你的仿函數有狀態,例如:

    class MyFunctor { 
        // Some fields here; 
    public: 
        MyFunctor(/* some parameters to set state */); 
        int operator()(SomeType &param) const; 
        // + some methods to retrieve result. 
    } 
    

    的C回調函數某種類型的用戶狀態參數(通常是無效*):

    void MyAlgorithmInC(SomeType *arr, 
            int (*fun)(SomeType *, void *), 
            void *user_state); 
    

    你可以寫一個正常的extern「C」函數,將其狀態參數轉換爲 你的函數對象:

    extern "C" int MyFunctorInC(SomeType *param, void *user_state) 
    { 
        MyFunctor *my_functor = (MyFunctor *)user_state; 
        return (*my_functor)(*param); 
    } 
    

    ,並使用它是這樣的:

    MyFunctor my_functor(/* setup parameters */); 
    MyAlgorithmInC(input_data, MyFunctorInC, &my_functor); 
    
  • 否則,只有正常的方式做到這一點 (正常如在「不產生在運行時機器代碼」等) 是使用一些靜態(全局)或線程本地存儲將函子 傳遞給外部「C」函數。 這限制了你可以用你的代碼做什麼,而且很醜,但會起作用。

+0

+1,詳細描述可能的情況。 – 2009-12-03 17:55:18

+0

+1,雖然回調簽名絕對不是C :) – quinmars 2009-12-03 22:23:20

+0

oops,nvm,不知何故,我混合了聲明,並認爲你在C中使用引用。 – quinmars 2009-12-03 23:24:49

2

我不認爲你可以:operator()在一個函數對象真的是一個成員函數,和C不知道這些事情。

,你應該能夠使用什麼是免費的C++函數,或者類的靜態功能。

0

這取決於如果這是一個靜態或實例方法,如果是靜態的,那麼你可以通過功能的className :: functionName,而如果它是一個實例方法是公平更加複雜,因爲你顯然需要配合在某些情況下,但不能以與在C#中的代表一樣的方式進行。

我發現這樣做的最好方法是創建一個持有類,該類使用實例化該對象以及函數指針,持有類可以直接調用該函數。

3

不,當然。 C函數的簽名採用參數作爲函數。

void f(void (*func)()) 
{ 
    func(); // Only void f1(), void F2(), .... 
} 

與仿函數的所有招數都使用模板功能:

template<class Func> 
void f (Func func) 
{ 
    func(); // Any functor 
} 
0

我會說不,因爲C++函數對象有一個重載的操作符()這是一個成員函數,並會因此需要一個成員函數指針。與普通的C函數指針相比​​,這是一種完全不同的數據類型,因爲它不能在沒有類的實例的情況下調用。您需要將一個普通函數或靜態成員函數傳遞給C庫。由於過載的()運算符不能是靜態的,所以你不能這樣做。您需要將C庫作爲普通非成員函數或靜態成員函數傳遞,然後您可以從中調用C++函子。

0

嗯,也許你可以寫一個免費的模板函數來包裝你的函數對象。如果他們都有相同的簽名,這應該工作。像這樣(未測試):

template<class T> 
int function_wrapper(int a, int b) { 
    T function_object_instance; 

    return funcion_object_instance(a, b); 
} 

這將適用於所有采用兩個整數並返回int的函數。

7

我發現這個 「gem」 使用谷歌。顯然可能,但我肯定不會推薦它。示例源代碼直接link

+0

請不要使用它。將你的函數包裝在一個常規的C函數中並不需要一個醜陋的黑客應該是微不足道的。 – 2009-12-03 18:03:37

+3

yuck,這確實是「黑魔法」作爲它提交的類別名稱建議 – 0xC0000022L 2011-06-23 17:57:32

+0

純寶石我認爲這是唯一的方法,因爲c調用者不知道如何推送額外的參數。函數是在具有這個「const」調用者的ram中創建的。 – 2013-04-10 19:38:24

3

用C++編寫A C回調函數必須被聲明爲extern "C"功能 - 因此使用仿函數是直接的。你需要編寫一些包裝函數來用作回調函數,並讓包裝函數調用函子。當然,回調協議需要有一些將上下文傳遞給函數的方式,以便能夠到達函子,或者任務變得非常棘手。大多數回調方案都有一種方式來傳遞上下文,但我曾與一些沒有的腦死亡工作。

看到這個答案的一些細節(以及在傳聞證據的意見看回調必須extern "C",而不僅僅是一個靜態成員函數):

0

許多C採用函數指針回調的API具有用戶狀態的void *參數。如果你有其中一個,那麼你很幸運 - 你可以使用一個Exterm C函數,將用戶數據視爲某種引用或鍵來查找函數,然後執行它。

否則,沒有。

1

GCC允許你成員函數指針轉換爲純函數指針(由純函數指針調用的函數的第一個參數是然後this)。

查看respective link in the manual

這需要-Wno-pmf-conversions標誌,以使相應的警告靜音以達到非常標準的功能。 C風格庫與C++風格編程接口非常方便。當成員函數指針是一個常量時,甚至不需要生成任何代碼:無論如何,API將使用該參數順序。

如果你已經有一個仿函數,以這種方式展平仿函數可能意味着將其展平爲operator(),給你一個必須用函子類指針本身作爲第一個參數調用的函數。這並不一定有幫助,但至少有C鏈接。

但至少當你沒有通過函數時,這是有幫助的,並提供了一個從<functional>std::mem_fn一個不廢話的C聯動替換。