9

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?
}
Adrian McCarthy
  • 45,555
  • 16
  • 123
  • 175
totten
  • 2,769
  • 3
  • 27
  • 41
  • 1
    Can't you just rename `external.c` into `external.cpp` and invoke the class's function? – Andy Prowl Feb 11 '13 at 15:31
  • 1
    There is lots of that C files. Can't rename. – totten Feb 11 '13 at 15:32
  • Well, how about `MyClass::sendCommandToSerialDevice();`? –  Feb 11 '13 at 15:32
  • @H2CO3: That's not a static function, so you can't invoke it that way. But I guess I see your point. – Andy Prowl Feb 11 '13 at 15:33
  • That isn't just a member; its an *instance* member. I.e. you need an *object* to go with it. You're in for more than you may first think if you want to make this a reality. – WhozCraig Feb 11 '13 at 15:33
  • To compile, I compile both files one by one with GCC and G++ since there is exist extern "C" keys. Since there is no batch compilation, I cannot use MyClass::sendComm...(); – totten Feb 11 '13 at 15:34
  • @AndyProwl O, I see. `Class method` means something like static methods for me (coming from Objective-C), sorry for the confusion. –  Feb 11 '13 at 15:34
  • @H2CO3 You're not the only one, I thought the same thing when I saw the Q-title, then I saw the member definition. – WhozCraig Feb 11 '13 at 15:35
  • @H2CO3: That's fine. I don't know Objective C, but I hate the word "method" anyway. I'm not sure where it comes from, maybe Java, but it sounds both ambiguous and meaningless to me. – Andy Prowl Feb 11 '13 at 15:35
  • @WhozCraig, I cannot create a new object because there can be only a connection to serial port. And connection is used by some other C++ file/class. – totten Feb 11 '13 at 15:35
  • 1
    @EnesUnal: Well, if that function is not `static`, you *will* need an object to invoke it on. Does not need to be a new one, but some object must exist. Otherwise, mark the function as `static`. – Andy Prowl Feb 11 '13 at 15:36
  • In any case you'll have to create some C linkage intermediary, say `extern "C" type intermediary(some args here) { return the_cpp_func(some args here); };` or some such (my C++ linkage-fu is rusty). – vonbrand Feb 12 '13 at 03:46
  • it was duplicate. Sorry for inconvenience, question may stay here since the answers may be useful. – totten Apr 13 '16 at 07:40

5 Answers5

21

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);
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 1
    could you please look at http://stackoverflow.com/questions/43537587/c-function-calling-c-member-function-where-the-c-code-is-compiled-with-a-c-c Thanks – Mawg says reinstate Monica Apr 21 '17 at 08:25
  • 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? – user48956 Apr 24 '20 at 16:07
10

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

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • 1
    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 Feb 11 '13 at 15:57
  • 1
    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 Feb 11 '13 at 16:00
  • @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 Feb 11 '13 at 16:16
  • @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 Feb 11 '13 at 16:17
  • 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 Feb 11 '13 at 16:35
  • 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. – datenwolf Feb 11 '13 at 16:38
  • @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 Feb 11 '13 at 16:56
  • @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.) – James Kanze Feb 11 '13 at 16:59
  • 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 ;) – datenwolf Feb 11 '13 at 17:31
  • 1
    @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. – James Kanze Feb 11 '13 at 18:00
2

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, ...);
}
datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • You can't use `dynamic_cast` on a `void*`. And how `main` gets its poionter to the object is another issue. – James Kanze Feb 11 '13 at 15:40
  • You cannot `dynamic_cast` from `void*` – David Rodríguez - dribeas Feb 11 '13 at 15:41
  • @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). – datenwolf Feb 11 '13 at 15:44
  • @datenwolf I use `static_cast`, but when casting from `void*`, both are guaranteed to have the same semantics. – James Kanze Feb 11 '13 at 15:46
  • @JamesKanze: I just realized you can write this even nicer and without any typecasts. See my edit. – datenwolf Feb 11 '13 at 15:53
  • @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. – James Kanze Feb 11 '13 at 16:23
  • 1
    @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. – datenwolf Feb 11 '13 at 16:26
  • @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 Feb 11 '13 at 16:54
  • 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 Feb 11 '13 at 17:25
1

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.

Mats Petersson
  • 126,704
  • 14
  • 140
  • 227
  • 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.) – James Kanze Feb 11 '13 at 15:42
  • @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. – datenwolf Feb 11 '13 at 16:02
  • Is that guaranteed, or just "most compilers do it that way"? – Mats Petersson Feb 11 '13 at 16:05
  • @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 Feb 11 '13 at 16:18
  • @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 Feb 11 '13 at 16:25
  • 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". – Mats Petersson Feb 11 '13 at 16:28
  • @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 Feb 11 '13 at 16:47
  • @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.). – James Kanze Feb 11 '13 at 16:50
  • Yes, that's my point. I too have used compilers like that. – Mats Petersson Feb 11 '13 at 16:51
  • 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 Feb 11 '13 at 17:26
0

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.

bames53
  • 86,085
  • 15
  • 179
  • 244