How to call a C++ method from C? [duplicate]

2020-02-01 02:38发布

问题:

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:

  1. 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.)

  2. 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.