user266009
user266009

Reputation:

How to call a c++ class and its method from a c file

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 class M 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

Answers (5)

GManNickG
GManNickG

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

quamrana
quamrana

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.

  1. C++ code can call any C code.
  2. C code usually cannot call any C++ code.
  3. C functions can be implemented by 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:

  1. The first code sample can only go in a .cpp file.
  2. The second code sample can only go in a .c file.

Additionally class M must be declared and defined in C++ files only.

Upvotes: 1

Billy ONeal
Billy ONeal

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 structure pointer goes in the C file. You are forced to use a structure 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

user257111
user257111

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

Bill Lynch
Bill Lynch

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

Related Questions