【发布时间】:2010-12-22 20:09:16
【问题描述】:
我正在尝试在 C++ 应用程序中使用 C 库,并在以下情况下发现了自己(我知道我的 C,但我对 C++ 还很陌生)。在 C 端,我有一组函数,它们以函数指针作为参数。在 C++ 方面,我有一个带有仿函数的对象,它与 C 函数所需的函数指针具有相同的签名。有没有什么办法可以将C++仿函数作为函数指针传递给C函数?
【问题讨论】:
我正在尝试在 C++ 应用程序中使用 C 库,并在以下情况下发现了自己(我知道我的 C,但我对 C++ 还很陌生)。在 C 端,我有一组函数,它们以函数指针作为参数。在 C++ 方面,我有一个带有仿函数的对象,它与 C 函数所需的函数指针具有相同的签名。有没有什么办法可以将C++仿函数作为函数指针传递给C函数?
【问题讨论】:
您不能直接将指向 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);
您可以编写一个普通的外部“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”函数。 这限制了您可以对代码执行的操作,虽然很丑陋,但会起作用。
【讨论】:
不,当然。 C 函数的签名将参数作为函数。
void f(void (*func)())
{
func(); // Only void f1(), void F2(), ....
}
模板函数使用所有带有仿函数的技巧:
template<class Func>
void f (Func func)
{
func(); // Any functor
}
【讨论】:
用 C++ 编写的 C 回调函数必须声明为 extern "C" 函数 - 所以直接使用函子是不可行的。您需要编写某种包装器函数以用作该回调并让该包装器调用函子。当然,回调协议需要有某种方式将上下文传递给函数,以便它可以到达函子,否则任务会变得非常棘手。大多数回调方案都有传递上下文的方法,但我使用过一些没有办法的脑残方案。
有关更多详细信息,请参阅此答案(并在 cmets 中查找有关回调必须是 extern "C" 而不仅仅是静态成员函数的轶事证据):
【讨论】:
我不认为你可以:函数对象中的operator() 实际上是一个成员函数,而 C 对此一无所知。
你应该能够使用的是免费的 C++ 函数,或者类的静态函数。
【讨论】:
GCC 允许您将成员函数指针转换为普通函数指针(普通函数指针调用的函数的第一个参数是this)。
查看respective link in the manual。
这需要-Wno-pmf-conversions 标志,以消除明显非标准功能的相应警告。非常方便将 C 风格库与 C++ 风格编程接口。当成员函数指针是常量时,这甚至根本不需要生成任何代码:API 无论如何都会使用该参数顺序。
如果你已经有一个仿函数,以这种方式展平仿函数可能意味着展平它的operator(),给你一个必须以仿函数类指针本身作为其第一个参数来调用的函数。这不一定有太大帮助,但至少有 C 链接。
但至少当您不使用函子时,这很有帮助,并为 <functional> 中的 std::mem_fn 提供了一个严肃的 C 链接替换。
【讨论】:
这取决于这是静态方法还是实例方法,如果是静态方法,则可以将函数作为 className::functionName 传递,如果是实例方法,则更复杂,因为您显然需要绑定某个实例,但不能以与 C# 等中的委托相同的方式执行此操作。
我发现这样做的最好方法是创建一个使用对象实例以及函数指针实例化的持有类,然后持有类可以直接调用该函数。
【讨论】:
我会说不,因为 C++ 仿函数有一个重载的运算符(),它是一个成员函数,因此需要一个成员函数指针。这是一种与普通 C 函数指针完全不同的数据类型,因为没有类的实例就无法调用它。您需要将普通函数或静态成员函数传递给 C 库。由于重载的() 运算符不能是静态的,所以你不能这样做。您需要向 C 库传递一个普通的非成员函数或静态成员函数,然后您可以从中调用 C++ 仿函数。
【讨论】:
嗯,也许你可以写一个免费的模板函数来包裹你的函数对象。如果他们都有相同的签名,这应该工作。像这样(未测试):
template<class T>
int function_wrapper(int a, int b) {
T function_object_instance;
return funcion_object_instance( a, b );
}
这适用于所有接受两个整数并返回一个整数的函数。
【讨论】:
许多采用函数指针回调的 C API 都有一个用于用户状态的 void* 参数。如果您有其中之一,那么您很幸运 - 您可以使用 exterm C 函数将用户数据视为某种引用或键来查找函子,然后执行它。
否则,不行。
【讨论】: