可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
This question already has an answer here:
Closed 3 years ago.
I have a C++ class and I'm compiling it with some C files.
I want to call a function which is defined in C++, actually in C++ class, so what am I going to do?
The following declarations to show what am I saying: there may there be syntax errors:
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?
}
回答1:
The common approach to this problem is providing a C wrapper API. Write a C function that takes a pointer to a MyClass
object (as MyClass
is not valid C, you will need to provide some moniker, simplest one is moving void*
around) and the rest of the arguments. Then inside C++ perform the function call:
extern "C" void* MyClass_create() {
return new MyClass;
}
extern "C" void MyClass_release(void* myclass) {
delete static_cast<MyClass*>(myclass);
}
extern "C" void MyClass_sendCommandToSerialDevice(void* myclass, int cmd, int params, int id) {
static_cast<MyClass*>(myclass)->sendCommandToSerialDevice(cmd,params,id);
}
Then the C code uses the C api to create the object, call the function and release the object:
// C
void* myclass = MyClass_create();
MyClass_sendCommandToSerialDevice(myclass,1,2,3);
MyClass_release(myclass);
回答2:
You'll have to pass an additional argument, with the address of
the object to call the function on. Something like:
extern "C" void SendCommandToSerialDevice( void* object,
int command, int parameters, int deviceId )
{
static_cast<MyClass*>( object)->sendCommandToSerialDevice(
command, parameters, deviceId );
}
main
will, of course, have to find the instance of the class
somehow.
EDIT:
Concerning some points brought up in other answers:
In your example, you compile main
as C. This is undefined
behavior, and in practice could mean that your constructors will
not be called on static objects. (If your code is in a DLL,
you're OK. The standard doesn't say anything about DLL's, but
in practice, they work.)
If you're reporting errors by means of exceptions, then
you'll have to change your signature to report them in some
other way, and wrap your code to catch all exceptions, and
convert them to the C convention. (Since your function has no
return value, this is easily handled by means of a return code.)
回答3:
If you want to do it correct
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, ...);
}
回答4:
You can't just go calling C++ code from C.
You will need to produce a C++ interface that can be called from C.
Something like this
// interface.h
#ifdef __cplusplus
extern "C" {
#endif
void createMyclass();
void callMyclassSendCommandToSerialDevice(int Command, int Parameters, int DeviceId);
void destroyMyclass();
#ifdef __cplusplus
extern }
#endif
Then you do this:
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;
}
Note that it's IMPERATIVE that you don't let "exceptions" through the wall to the C code, as that is definite undefined behaviour.
回答5:
You cannot invoke a C++ method directly in C. Instead you may create a C wrapper and then call that:
C/C++ compatible header file:
#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
implementation (a .cpp file)
#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);
}
Of course since resource and error handling can be different between C and C++ code you'll have to work out how to handle the combination in your case. For example the above just leaks a MyClass
object instead of cleaning up, and doesn't do anything about exceptions.