如何从C调用C ++方法?
我有一个C ++类,我正在用一些C文件编译它。
我想调用一个用C ++定义的函数,实际上是在C ++类中,所以我该怎么做?
以下声明显示我在说什么:可能存在语法错误:
serial_comm.cpp
class MyClass { void sendCommandToSerialDevice(int Command, int Parameters, int DeviceId) { //some codes that write to serial port. } }
external.c
int main(int argc, char ** argv) { //what am I going to write here? }
解决此问题的常见方法是提供C包装器API。 编写一个带有指向MyClass
对象的指针的C函数(因为MyClass
不是有效的C,你需要提供一些名字,最简单的就是移动void*
)和其余的参数。 然后在C ++里面执行函数调用:
extern "C" void* MyClass_create() { return new MyClass; } extern "C" void MyClass_release(void* myclass) { delete static_cast(myclass); } extern "C" void MyClass_sendCommandToSerialDevice(void* myclass, int cmd, int params, int id) { static_cast(myclass)->sendCommandToSerialDevice(cmd,params,id); }
然后C代码使用C api创建对象,调用函数并释放对象:
// C void* myclass = MyClass_create(); MyClass_sendCommandToSerialDevice(myclass,1,2,3); MyClass_release(myclass);
您必须传递一个额外的参数,并使用该对象的地址来调用该函数。 就像是:
extern "C" void SendCommandToSerialDevice( void* object, int command, int parameters, int deviceId ) { static_cast( object)->sendCommandToSerialDevice( command, parameters, deviceId ); }
当然, main
意志必须以某种方式找到该类的实例。
编辑:
关于在其他答案中提出的一些观点:
-
在您的示例中,您将
main
编译为C.这是未定义的行为,实际上可能意味着您的构造函数不会在静态对象上调用。 (如果你的代码在DLL中,你就可以了。标准没有说明DLL的任何内容,但在实践中,它们可以工作。) -
如果您通过exception报告错误,那么您将不得不更改签名以其他方式报告它们,并包装代码以捕获所有exception,并将它们转换为C约定。 (由于您的函数没有返回值,因此可以通过返回代码轻松处理。)
如果你想做正确的话
serial_comm_wrapper.h
#ifdef __cpluscplus class MyClass; extern "C" { #else struct MyClass; typedef struct MyClass MyClass; #endif MyClass *MyClass_new(); void MyClass_sendCommandToSerialDevice(MyClass *instance, int Command, int Parameters, int DeviceId); #ifdef __cpluscplus } #endif
serial_comm_wrapper.cc
#include "serial_comm_wrapper.h" #include "serial_comm.hh" MyClass *MyClass_new() { return new MyClass(); } void MyClass_sendCommandToSerialDevice(MyClass *instance, int Command, int Parameters, int DeviceId) { instance->sendCommandToSerialDevice(command, Parameters, DeviceID); }
external.c
#include "serial_comm_wrapper.h" int main(int argc, char ** argv) { MyClass *instance = MyClass_new(); MyClass_sendCommandToSerialDevice(instance, ...); }
你不能只是从C调用C ++代码。
您将需要生成可以从C调用的C ++接口。
像这样的东西
// interface.h #ifdef __cplusplus extern "C" { #endif void createMyclass(); void callMyclassSendCommandToSerialDevice(int Command, int Parameters, int DeviceId); void destroyMyclass(); #ifdef __cplusplus extern } #endif
然后你这样做:
static MyClass *myclass; void createMyclass() { try { myclass = new MyClass; } catch(...) { fprintf(stderr, "Uhoh, caught an exception, exiting...\n"); exit(1); } } void callMyclassSendCommandToSerialDevice(int Command, int Parameters, int DeviceId) { // May need try/catch here. myclass->sendCommandToSerialDevice(Command, Parameters, DeviceId); } void destroyMyclass() { delete myclass; }
请注意,您不要让“exception”通过墙到C代码,因为这是明确的未定义行为。
您不能直接在C中调用C ++方法。相反,您可以创建一个C包装器,然后调用它:
C / C ++兼容头文件:
#ifdef __cplusplus extern "C" { #endif struct MyClass; MyClass *new_MyClass(); void MyClass_sendCommandToSerialDevice(MyClass *c, int Command, int Parameters, int DeviceID); #ifdef __cplusplus } // extern "C" #endif
实现(.cpp文件)
#include "my_c_compatiblity_header.h" #include "MyClass.h" extern "C" MyClass *new_MyClass() { return new MyClass(); } extern "C" void MyClass_sendCommandToSerialDevice(MyClass *c, int Command, int Parameters, int DeviceID) { c->sendCommandToSerialDevice(Command, Parameters, DeviceID); }
main.c中
int main(int argc, char ** argv) { MyClass *c = new_MyClass(); MyClass_sendCommandToSerialDevice(c, 1, 0, 123); }
当然,由于C和C ++代码之间的资源和error handling可能不同,因此您必须确定如何在您的情况下处理组合。 例如,上面只是泄漏了一个MyClass
对象而不是清理,并且没有对exception做任何事情。