How to call a C++ method from C?

30,143

Solution 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);

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

Solution 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, ...);
}

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

Share:
30,143
totten
Author by

totten

nothing.

Updated on July 09, 2022

Comments

  • totten
    totten almost 2 years

    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?
    }
    
  • James Kanze
    James Kanze about 11 years
    You can't use dynamic_cast on a void*. And how main gets its poionter to the object is another issue.
  • David Rodríguez - dribeas
    David Rodríguez - dribeas about 11 years
    You cannot dynamic_cast from void*
  • James Kanze
    James Kanze about 11 years
    With regards to exceptions---it's a good thing to mention, but in general, you can count on them passing right through the C. (On the other hand, his example compiles the main in C, which could mean that constructors to static objects are not called.)
  • datenwolf
    datenwolf about 11 years
    @JamesKanze: Okay I corrected it. Using C style type casting now (I don't know if a static_cast or reinterpret_cast would be better suited).
  • James Kanze
    James Kanze about 11 years
    @datenwolf I use static_cast, but when casting from void*, both are guaranteed to have the same semantics.
  • datenwolf
    datenwolf about 11 years
    @JamesKanze: I just realized you can write this even nicer and without any typecasts. See my edit.
  • datenwolf
    datenwolf about 11 years
    Regarding concern 1) technically it's not main that does static initialization, but the runtime environment that ultimately calls main after performing the initialization. As long as you link your executable with a C++ runtime you should be fine. For example you could compile main.c with a C compiler, .e.g. cc -c main.c but then link the binary as C++, e.g. c++ -o exe main.o serial_comm.o
  • datenwolf
    datenwolf about 11 years
    Also when linking a program that contains C++ compilation units you actually can not avoid doing it through the C++ path, as the C++ compilation units will contain references to symbols that would be missing going just through the C linker path. This is a standard SNAFU when using libraries implemented in C++ but having a C frontend; trying to use those in a pure C program yields a ton of undefined references; the default solution is to call the linker in C++ mode.
  • datenwolf
    datenwolf about 11 years
    @JamesKanze: See my comment your answer. It's not the compilation unit containing main, that does the static initialization, but the runtime library, that will eventually also call main. If your program gets linked with the C++ runtime you're fine. You can implement main in a C compilation unit just fine.
  • Mats Petersson
    Mats Petersson about 11 years
    Is that guaranteed, or just "most compilers do it that way"?
  • James Kanze
    James Kanze about 11 years
    @datenwolf How the runtime does the initialization is implementation defined. C++ has a lot of restrictions compared to C, because in early implementations, the C++ compiler recognized the function main, and inserted the initialization code there. I suspect that most modern implementations use the same technique they utilise for DLLs, so it will probably work, but formally, it's undefined behavior.
  • James Kanze
    James Kanze about 11 years
    @datenwolf And I think you are confusing one implementation (g++?) with what the standard requires, and what has been done in other implementations.
  • James Kanze
    James Kanze about 11 years
    @datenwolf Your comment is wrong. You just describe how one particular implementation does it. Most modern implementations probably do something similar, because they want to support C++ in DLLs, and need the technology, but I've used implementations which didn't.
  • James Kanze
    James Kanze about 11 years
    @datenwolf That would be struct MyClass* instance.... This is C, after all. Formally, I'm not sure that it isn't undefined behavior as well---the struct MyClass in C is not the class in C++. But practically, I can't imagine it not working.
  • datenwolf
    datenwolf about 11 years
    @JamesKanze: I know of no implementation where the main function would be the process entry point (unless you explicitly told the linker to do so). Even all the embedded system compilers I know, main is called through the runtime system. I know that at least the following implementations work that way. MSVC++, GNU C++, DMC++, Borland C++, Intel Compiler Suite and Open Watcom.
  • datenwolf
    datenwolf about 11 years
    @JamesKanze: Didn't you notice my typedef? As for what happens on the C side: A C opaque pointer to a struct behaves no differently than a void*, so this is exactly the same as writing void* without the explicit casts. Also the C standard asserts that casting a void* to a qualified type pointer will not change the pointer's value. So basically this is just semantic sugar. But very nice sugar. On the C side the compiler sees just some pointer (with the value a C++ code created), which value it won't touch and can't dereference. But on the C++ side that pointer has actual meaning.
  • Mats Petersson
    Mats Petersson about 11 years
    I don't think it is so much a matter of "where is main called from" - I completely agree, something other than main is the entry point for the program. However, that in itself doesn't mean that main doesn't have the code to "call the constructors for static objects".
  • datenwolf
    datenwolf about 11 years
    AFAIK the standard requires that static initializers and constructors to be executed before main is called. The standard defines main as the point of entry for program implementation, but explicitly requires all implementation to have happend before. This can happen only (and only) when this is performed by code that will ultimately call main. However main is not the function the OS will call. The OS calls some runtime environment executable entry function.
  • datenwolf
    datenwolf about 11 years
    For example in Windows the runtime entry functions are called _mainCRTStartup (calling int main(int, char*[])) and _WinMainCRTStartup (calling int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)). It's also important to understand, that is this very runtime that is responsible for accepting the value returned by main or WinMain and to pass it to the OS so that other processes can receive it. Process exit codes are handled in a OS dependant manner.
  • James Kanze
    James Kanze about 11 years
    @datenwolf main is what the compiler wants it to be. The original C++ compiler (CFront) certainly added code to main to call the constructors (and used atexit to call the destructors). I've used others which worked this way as well.
  • James Kanze
    James Kanze about 11 years
    @MatsPetersson In the early days of C++, most compilers just used the standard crt0, which was also used by C. And which didn't have any provision for doing anything except for setting up the required C objects (stdin, etc.) and calling main. So C++ compilers inserted the necessary initialization code in main. I've used such compilers, so I know that they existed. And I know that the standards committee took steps to ensure that they were legal (e.g. you cannot call main recursively, etc.).
  • Mats Petersson
    Mats Petersson about 11 years
    Yes, that's my point. I too have used compilers like that.
  • James Kanze
    James Kanze about 11 years
    @datenwolf I missed the typedef, but frankly, I wouldn't bother. The opaque struct type is the standard C idiom. And there is an enormous difference between a pointer to an opaque struct and a void*. (For starters, they aren't necessarily the same size.) In theory, a really sophisticated linker could detect the error. (The notion of identical types is different in the two languages.) In practice, it will work; I'd use it without hesitation (and there are very, very few cases of UB where I'd say that).
  • James Kanze
    James Kanze about 11 years
    @datenwolf First, the standard doesn't say that they will be executed before main is entered. But that's beside the point, because from your point of view, main is entered when the first line of code in it starts to execute. The initialization routine is called before any code starts to execute. Implementations have used this strategy (CFront, for example), and the standard is carefully designed to make it legal.
  • James Kanze
    James Kanze about 11 years
    @datenwolf I know what VC++ does today. It's largely irrelevant, however, with regards to what the standard requires. (The same thing could be said for g++, or just about any other compiler.)
  • datenwolf
    datenwolf about 11 years
    According to ISO 9899:201x (the draft for C11) §6.3.2.3-7: "A pointer to an object type may be convertex to a pointer to a different object type (…)" (that would be conversion to opaque struct) "(…), when converted back again, the result shall compare equal to the original pointer", this basically states, that, at least from the point of view of C, using an opaque struct pointer to pass a C++ class instance pointer around is completely valid (in my interpretation). Any C++ experts want to give insight on the opinion the C++ standard has on this?
  • datenwolf
    datenwolf about 11 years
    Ugh, just another bullet point to add to my list of why I don't like C++; doing crazy things with main I mean.
  • datenwolf
    datenwolf about 11 years
    I really wonder, when you're going to implement a compiler for a new language, what's more difficult: Adjusting the runtime environment, maybe adding a new internal symbol, that's a NoOP for non-C++ compilations, or going the long way of messing with main. Whoever came up with the later method (that made it into the standard) should check his medication. Okay, today I've learnt something new. And I have found yet another reason, why the creators of C++ should have themself checked for mental illnesses ;)
  • James Kanze
    James Kanze about 11 years
    @datenwolf Having actually worked on similar projects, and knowing something of the environment where C++ was developed, doing some meta-magic with main was by far the simplest solution.
  • Mawg says reinstate Monica
    Mawg says reinstate Monica about 7 years
    could you please look at stackoverflow.com/questions/43537587/… Thanks
  • user48956
    user48956 about 4 years
    Hmm -- this is pretty intrusive for C. Let's say you have a C API who's interface is provided a nullary (e.g. f()). This would mean providing a new function declaration who's definition reference the object instance. It there any way to form nullary closures with references to global data? (i.e. given some instance x and method f), can I produce a nullary C function pointer f that calls x.m(), without a global reference to x?