对于C++对象,此 C 回调是否安全
Is this C callback safe with C++ objects?
我的目的是从我的C++代码中调用一些C函数并传递一些C++对象。事实上,我正在使用GSL库的集成例程(用C编写(,请参阅此链接,
我的代码片段:
// main.cpp
#include <stdio.h>
#include <gsl/gsl_integration.h>
#include <myclass.h>
/* my test function. */
double testfunction ( double x , void *param ) {
myclass *bar=static_cast<myclass*>(param);
/*** do something with x and bar***/
return val;
}
int main ( int argc , char *argv[] ) {
gsl_function F; // defined in GSL: double (* function) (double x, void * params)
/* initialize.*/
gsl_integration_cquad_workspace *ws =
gsl_integration_cquad_workspace_alloc( 200 ) ;
/* Prepare test function. */
myclass foo{}; // call myclass constructor
F.function = &testfunction;
F.params = &foo;
/* Call the routine. */
gsl_integration_cquad( &F, 0.0,1.0,1.0e-10,1.0e-10,ws, &res,&abserr,&neval);
/* Free the workspace. */
gsl_integration_cquad_workspace_free( ws );
return 0;
}
就我而言,直接调用gsl_integration_cquad似乎没问题,只要标头包含像"ifdef __cplusplus"这样的 sth,我关心的是最初在 C 中定义的回调 F,我是否可以以这种方式传递 testfunction 和 C++ foo 对象?
或者有没有更好的方法来做这种事情,也许是重载并使用函子?
附言我可以在回调函数中进行执行处理吗?(在"测试函数"中使用 try catch(。它适用于我的情况,但不确定它是否合法。
我不熟悉有问题的库,但总的来说,传递指向回调的指针和 void*
时一个C例程,它将用void*
回调,您需要做两件事来确保其安全:
-
您传递其地址的函数必须声明为外部"C"。不用很多编译器就可以侥幸逃脱,但是这是不合法的,一个好的编译器会抱怨。
-
转换为
void*
的类型必须完全相同键入作为在回调中将其转换回的类型。 这经典错误是将类似new Derived
的内容传递给C 函数,并将其强制转换为回调中的Base*
。 这往返Derived*
→void*
→Base*
未定义行为。 它在某些时候会起作用,但在其他时候,它会可能会崩溃,或导致任何其他问题。 -
正如 cdhowie 在评论中指出的那样,您不想允许异常在 C 代码中传播。 再次,它可能有效。 但未必。
对于您发布的确切示例,您唯一需要做的就是就是宣告testfunction
是extern "C"
,你们都是右。 如果以后开始使用多态对象,但是,请注意第二点。
myclass *bar=static_cast<myclass*>(param);
与void*
.
如果您的意思是通过 c 回调的 void*
指针传输 c++ 类指针,是的,执行static_cast<>
是安全的。
当通过 c 代码传递时,不会丢失此类指针的 c++ 特定属性。尽管传递派生类指针并将静态强制转换回基类,但正如 Kanze 指出的那样@James无法正常工作。
void*
可能只是由 C 库传递,而不查看指向的数据,因此如果它包含C++类,这不是问题。当您将void*
正确投射到正确的日志时,应该没有任何问题。
为了确保回调函数本身兼容,可以将其声明为 extern "C"
。此外,还应确保回调函数不会引发异常,因为调用回调的 C 代码不会期望这些异常。
总之,我将代码拆分为一个执行实际工作的函数和另一个用作回调并处理与 C 库的接口的函数,例如:
#include <math.h>
double testfunction ( double x ,myclass *param ) {
/*** do something with x and bar***/
return val;
}
extern "C" double testfunction_callback ( double x , void *param ) {
try {
myclass *bar=reinterpret_cast<myclass*>(param);
return testfunction(x, bar);
}
catch(...) {
std::cerr << "Noooo..." << std::endl;
return NAN;
}
}
- 如果 C 函数仍然可以间接执行(通过回调函数),那么将它声明为静态函数是否是一种不好的做法?
- 从其存储的回调中删除 std::函数是否安全
- 如果事件在仍在执行时再次设置,RegisterWaitForSingleObject 是否会并行运行回调?
- 是否可以影响 C++ 中回调函数的局部变量?
- libevent是否允许在不同的线程中运行timer/io的回调
- SetTimer (带有回调函数)是否通过启动新线程来工作?
- 如何知道 sqlite 中的函数"回调"是否返回了一些东西?
- 是否可以使用包含{sub,super}类函数指针的回调表
- 破坏vulkan以测试调试回调是否正确设置的好方法是什么
- 当满足条件时,是否可以在 GLSL 着色器中回调 C/C++ 函数/代码
- 是否可以在C 中使回调函数返回值
- 重定向是否有任何回调函数
- 自lambdas进入C++以来,“回调”接口是否已过时
- SleepEx 是否保证在超时之前调用所有挂起的完成回调
- 是否应删除回调传递的void*指针
- hiredis Redis 库是否为异步回调创建了自己的线程
- 是否可以调用 WriteFile,应用程序将永远等待回调
- ZeroMQ是否有数据到达时的通知/回调事件/消息
- 是否可以将 bind() *this 绑定到类成员函数以回调 C API
- 对于C++对象,此 C 回调是否安全