zeroMQ does it that way. The library is written in C++, but the library API is a C interface. In principle, you hide classes in memory blocks of the object size and pass void*
around. For a few simple function, e.g. sleep_for
, you can just create a wrapper function. In your header, declare
extern "C" {void sleep_for(int duration);}
In your cpp-file, implement this in terms of std::this_thread::sleep_for
.
sleep_for(int duration) {std::this_thread::sleep_for( std::chrono::milliseconds(duration);}
For classes, you need C function to create a new object, destruct it and of course to call member functions. For a class A, this could look as follows:
C header file "A_C_API.h":
extern "C" {
enum {SIZE_OF_A = 4};
typedef char[SIZE_OF_A] a_class;
a_class initA(void* a, int x);
void destructA(void* a);
int callGet(void *a);
}
C++ source file:
class A {
public:
A(int i): x{i} {}
int get() const {return x;}
private:
int x;
};
static_assert( SIZE_OF_A == sizeof(A), "size mismatch!");
void initA(void* a, int x) {
new (a) A(x);
}
void destructA(void* a) {
static_cast<A*>(a)->~A();
}
int callGet(void *a) {
return static_cast<A*>(a)->get();
}
In your C application, you can then
#include "A_C_API.h"
int main(int argc, char* argv[]) {
a_class obj;
initA(&obj);
printf("%d", callGet(&obj) );
destructA( &obj );
return 0;
}
You will have to catch exceptions and convert them to some C error handling. You will also have to handle alignment issues on some platforms, especially SPARK platforms. I have omitted this for simplicity, but you can take a look at the zeromq header how to do this.