2009-12-03 152 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联动替换。