我正在尝试在C ++应用程序中使用C库,并在以下情况下找到了自己(我知道我的C,但我对C ++很新)。在C方面,我有一组函数,它们以函数指针作为参数。在C ++方面,我有一个带有仿函数的对象,它与C函数所需的函数指针具有相同的签名。有没有办法使用C ++仿函数作为传递给C函数的函数指针?
答案 0 :(得分:51)
您不能直接将指向C ++仿函数对象的指针作为C代码的函数指针传递 (甚至是C ++代码)。
此外,为了便于将回调传递给C代码,至少需要声明它
作为extern "C"
非成员函数。
至少,因为某些API需要特定的函数调用约定
附加声明修饰符。
在许多环境中,C和C ++具有相同的调用约定,仅有所不同
在名称修改中,所以任何全局函数或静态成员都可以工作。
但是你仍然需要在正常函数中将调用包装到operator()
。
如果你的仿函数没有状态(它只是满足一些正式的对象 要求等):
class MyFunctor {
// no state
public:
MyFunctor();
int operator()(SomeType ¶m) 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 ¶m) const;
// + some methods to retrieve result.
}
和 C回调函数采用某种用户状态参数(通常为void *):
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”功能。 这限制了您可以对代码执行的操作,并且很难实现但是可以正常工作。
答案 1 :(得分:7)
答案 2 :(得分:5)
不,当然。 C函数的签名将参数作为函数。
void f(void (*func)())
{
func(); // Only void f1(), void F2(), ....
}
所有使用仿函数的技巧都由模板函数使用:
template<class Func>
void f (Func func)
{
func(); // Any functor
}
答案 3 :(得分:3)
用C ++编写的C回调函数必须声明为extern "C"
函数 - 因此直接使用仿函数。您需要编写某种包装函数作为该回调使用并让该包装器调用该仿函数。当然,回调协议需要有一些方法将上下文传递给函数,以便它可以到达仿函数,或者任务变得非常棘手。大多数回调方案都有一种传递上下文的方法,但是我已经使用了一些没有回归的脑死亡方法。
有关更多详细信息,请参阅此答案(并在评论中查看回调必须为extern "C"
的轶事证据,而不仅仅是静态成员函数):
答案 4 :(得分:2)
我认为你不能:函数对象中的operator()
实际上是一个成员函数,C对这些函数一无所知。
您应该能够使用的是免费的C ++函数或类的静态函数。
答案 5 :(得分:1)
GCC允许您将成员函数指针转换为普通函数指针(由普通函数指针调用的函数的第一个参数为this
)。
查看respective link in the manual。
这需要-Wno-pmf-conversions
标志,以便为明确的非标准功能静音相应的警告。将C样式库与C ++样式编程接口非常方便。当成员函数指针是常量时,甚至根本不需要生成任何代码:API无论如何都会使用该参数顺序。
如果你已经有一个仿函数,以这种方式展平仿函数可能意味着展平它的operator()
,给你一个函数,必须用一个仿函数类指针本身作为它的第一个参数。这并不一定能帮到那么多,但至少有C联系。
但至少当你没有通过仿函数时,这很有帮助,并为来自std::mem_fn
的{{1}}提供了一个毫无意义的C链接替换。
答案 6 :(得分:0)
这取决于它是静态方法还是实例方法,如果它是静态的,那么你可以将函数作为className :: functionName传递,如果它是一个实例方法则更复杂,因为你显然需要绑定到某个实例但不能像在C#等中使用代理那样进行。
我发现这样做的最好方法是创建一个使用对象实例和函数指针实例化的保持类,然后保持类可以直接调用该函数。
答案 7 :(得分:0)
我会说不,因为C ++仿函数有一个重载的运算符()
,它是一个成员函数,因此需要一个成员函数指针。这是一种与普通C函数指针完全不同的数据类型,因为没有该类的实例就无法调用它。您需要将普通函数或静态成员函数传递给C库。由于重载的()
运算符不能是静态的,因此您无法执行此操作。您需要将C库传递给普通的非成员函数或静态成员函数,然后您可以从中调用C ++函子。
答案 8 :(得分:0)
嗯,也许你可以编写一个包含你的函数对象的免费模板函数。如果他们都有相同的签名,这应该工作。像这样(未经测试):
template<class T>
int function_wrapper(int a, int b) {
T function_object_instance;
return funcion_object_instance( a, b );
}
这适用于所有采用两个整数并返回int的函数。
答案 9 :(得分:0)
许多采用函数指针回调的C API都有一个用于用户状态的void *参数。如果你有其中一个,那么你很幸运 - 你可以使用exterm C函数将用户数据视为某种参考或键来查找仿函数,然后执行它。
否则,不。