Reputation:
I am trying to access a C++ class and call its method from a .c
file.
I google this topic and find this http://developers.sun.com/solaris/articles/mixing.html
It says:
You can write
extern "C"
functions in C++ that access classM
objects and call them from C code.
Here is a C++ function designed to call the member function foo
:
extern "C" int call_M_foo(M* m, int i) { return m->foo(i); }
My question is where do I put the about line? In my C++ .h
file? Or C .h
file?
And it goes on and says:
Here is an example of C code that uses class M
:
struct M; // you can supply only an incomplete declaration
int call_M_foo(struct M*, int); // declare the wrapper function
int f(struct M* p, int j) // now you can call M::foo
{
return call_M_foo(p, j);
}
But how/where do I create the class M
in my C file?
And where do I put the above code? C .h
file? C++ .h
file? Or C .c
file?
Thank you.
Thank you for GMan's detailed answer.
I did follow your suggestion. But I get compile error in my .c
file.
main.c:33:
./some_class.h:24: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘attribute’ before ‘’ token
./some_class.h:25: error: expected ‘)’ before ‘’ token
./some_class.h:26: error: expected ‘)’ before ‘*’ token
And here are my some_class.h
line 24-26
:
#ifdef __cplusplus
class M {
public:
M();
virtual ~M();
void method1(char* name, char* msg);
};
extern "C" {
#else
struct M;
#endif
/* access functions line 24-26 are here*/
M* M_new(void);
void M_delete(M*);
void M_method1(M*, char*, char*);
#ifdef __cplusplus
}
#endif
For some reason, my C compiler does not like extern "C"
in GMan's original some_test.h
. So I have to modify to above. It seems like the C compiler does not like/understand the struct M;
line.
Any idea will be much appreciated.
Upvotes: 16
Views: 23696
Reputation: 503953
Your header file, which is shared between your C and C++ code:
#ifdef __cplusplus // only actually define the class if this is C++
class some_class
{
public:
int some_method(float);
};
#else
// C doesn't know about classes, just say it's a struct
typedef struct some_class some_class;
#endif
// access functions
#ifdef __cplusplus
#define EXPORT_C extern "C"
#else
#define EXPORT_C
#endif
EXPORT_C some_class* some_class_new(void);
EXPORT_C void some_class_delete(some_class*);
EXPORT_C int some_class_some_method(some_class*, float);
Then your source file:
#include "some_foo.h"
int some_class::some_method(float f)
{
return static_cast<int>(f);
}
// access functions
EXPORT_C some_class* some_class_new(void)
{
return new some_class();
}
EXPORT_C void some_class_delete(some_class* this)
{
delete this;
}
EXPORT_C int some_class_some_method(some_class* this, float f)
{
return this->some_method(f);
}
Now compile that source, and link to it. Your C source would be something like:
#include "some_class.h"
some_class* myInstance = some_class_new();
int i = some_class_some_method(myInstance, 10.0f);
some_class_delete(myInstance);
If you're serious about mixing C and C++, you'll want macro's.
Here are some sample macro's that would make this much easier:
// in something like c_export.h
// extern "C" macro
#ifdef __cplusplus
#define EXPORT_C extern "C"
#else
#define EXPORT_C
#endif
// new
#define EXPORT_C_CLASS_NEW(classname) EXPORT_C \
classname * classname##_new(void)
#define EXPORT_C_CLASS_NEW_DEFINE(classname) \
EXPORT_C_CLASS_NEW(classname) \
{ return new classname (); }
// repeat as much as you want. allows passing parameters to the constructor
#define EXPORT_C_CLASS_NEW_1(classname, param1) EXPORT_C \
classname * classname##_new( param1 p1)
#define EXPORT_C_CLASS_NEW_1_DEFINE(classname, param1) \
EXPORT_C_CLASS_NEW_1(classname, param1) \
{ return new classname (p1); }
// delete
#define EXPORT_C_CLASS_DELETE(classname) EXPORT_C \
void classname##_delete( classname * this)
#define EXPORT_C_CLASS_DELETE_DEFINE(classname) \
EXPORT_C_CLASS_DELETE(classname) \
{ delete this; }
// functions
#define EXPORT_C_CLASS_METHOD(classname, methodname, ret) EXPORT_C \
ret classname##_##methodname##( classname * this)
#define EXPORT_C_CLASS_METHOD_DEFINE(classname, methodname, ret) \
EXPORT_C_CLASS_METHOD(classname, methodname, ret) \
{ return this->##methodname##(); }
// and repeat as necessary.
#define EXPORT_C_CLASS_METHOD_1(classname, methodname, ret, param1) EXPORT_C \
ret classname##_##methodname( classname * this, param1 p1)
#define EXPORT_C_CLASS_METHOD_1_DEFINE(classname, methodname, ret, param1) \
EXPORT_C_CLASS_METHOD_1(classname, methodname, ret, param1) \
{ return this->##methodname##(p1); }
And so on. Our header/source becomes:
// header
#include "c_export.h" // utility macros
#ifdef __cplusplus // only actually define the class if this is C++
class some_class
{
public:
int some_method(float);
};
#else
// C doesn't know about classes, just say it's a struct
typedef struct some_class some_class;
#endif
// access functions
EXPORT_C_CLASS_NEW(some_class);
EXPORT_C_CLASS_DELETE(some_class);
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float);
// source
#include "some_foo.h"
int some_class::some_method(float f)
{
return static_cast<int>(f);
}
// access functions
EXPORT_C_CLASS_NEW_DEFINE(some_class);
EXPORT_C_CLASS_DELETE_DEFINE(some_class);
EXPORT_C_CLASS_METHOD_1_DEFINE(some_class, some_method, int, float);
And that's much more concise. It could be made simpler (possibly) with variadic macro's, but that's non-standard and I leave that to you. :] Also, you can make macro's for normal non-member functions.
Note that C does not know what references are. If you want to bind to a reference, your best bet is probably just to write the export definition manually. (But I'll think about it, maybe we can get it automatically).
Imagine our some_class
took the float
by (non-const)reference (for whatever reason). We'd define the function like so:
// header
// pass by pointer! v
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float*) ;
// source
EXPORT_C_CLASS_METHOD_1(some_class, some_method, int, float*)
{
// dereference pointer; now can be used as reference
return this->some_method(*p1);
}
And there we go. C would interface with references with pointers instead:
// c source, if some_method took a reference:
float f = 10.0f;
int i = some_class_some_method(myInstance, &f);
And we pass f
"by reference".
Upvotes: 31
Reputation: 39374
All the information you need is in the link you provide. You just need to understand that there needs to be a strict separation between C and C++ code.
The key part to understand is that the C and C++ compilers mangle function names when making object files in different ways, so they would normally not be able to interoperate (at link time), except that C++ can be prompted to know the difference by using extern "C"
The prototype:
void f(int);
might be mangled by a C compiler to: _f
, but a C++ compiler might choose a very different name eg f_int
, and so the linker would not know they are supposed to be the same.
However:
extern "C" void f(int);
would be mangled by a C++ compiler to _f
, but a C compiler would choke on the extern "C"
. To avoid this you should used something like this:
#ifdef __cplusplus
extern "C" {
#endif
void f(int);
#ifdef __cplusplus
} /* closing brace for extern "C" */
#endif
Now the whole of the above section can live in a .h file and is, as the sun.com
article states, a mixed-language header.
This means that a .c or .cpp file can #include
this header and code can call f();
and either a .c or .cpp file can #include
this header and implement it:
void f()
{
}
Now the good bit is that a .cpp file can implement this to call any C++ code it likes.
Now to answer your specific questions:
Additionally class M must be declared and defined in C++ files only.
Upvotes: 1
Reputation: 106549
You have several questions combined here so I will answer them individually.
My question is where do I put the about line? In my c++ .h file? or c .h file?
The extern "C"
line goes in the C++ file. It essentially tells the compiler to
limit everything whithin the extern "C"
block to the C subset of C++, and to
export functions declared in this area accordingly.
But how/where do I create the class M in my c file?
You can't. C does not have the concept of classes, and there's absolutely no way to instantiate a class directly. You essentially have to export a C function in your C++ file which creates the class and returns it as a pointer. Then you can pass that pointer around your C application. You can't actually modify the class directly in your C application, because C does not support classes, and your C++ compiler may insert "hidden" variables for bookkeeping inside the actual declaration of the class.
And where do I put the above code?
The piece of code that uses a struct
ure pointer goes in the C file. You are
forced to use a struct
ure pointer because C does not support classes at all.
You can put function calls using that function anywhere in a C implementation
file, just like normal C function calls.
Upvotes: 2
Reputation:
The site you have linked to has the answer already:
You can declare function print in a header file that is shared by C and C++ code:
#ifdef __cplusplus extern "C"
#endif int print(int i, double d);
You can declare at most one function of an overloaded set as extern "C" Here is the example C header for the wrapper functions:
int g_int(int);
double g_double(double);
Basically, there can be a header shared between the two that declares the function prototype, adding the extern "C" modifier if you are in C++ to ensure the function can be accessed in an object from C. You define the body of the function later on in the C++ code as usual, if necessary inside a class etc, and you use the function in C like normal.
Upvotes: 0
Reputation: 81936
You need to split it among the C++ header and implementation files.
foo.h:
extern "C" int call_M_foo(M* m, int i);
foo.cc:
extern "C" int call_M_foo(M* m, int i) {
return m->foo(i);
}
To create the object of type M, you would need a similar function:
foo.h:
struct M;
extern "C" M* create_M();
foo.cc:
extern "C" M* create_M() {
return new M;
}
Upvotes: 4