.NET Forum / Languages / Managed C++ / April 2006
C interface to C++ classes
|
|
Thread rating:  |
Bit byte - 18 Apr 2006 12:55 GMT I have a set of C++ classes for which I want to provide a C API - and compile into a C DLL, for use in an application that can only work with a C interface DLL.
Any suggestions/pointers on how to proceed. Googling is not bringng anything up that directly addresses the issue (parashift info is useful, but does not deal specifically, with the isse)
Bruno van Dooren - 18 Apr 2006 13:40 GMT > I have a set of C++ classes for which I want to provide a C API - and > compile into a C DLL, for use in an application that can only work with [quoted text clipped - 3 lines] > anything up that directly addresses the issue (parashift info is useful, > but does not deal specifically, with the isse) You are not using LabVIEW by any chance? :-) I have done something similar, and I created a dll with C functions that forwarded the arguments to C++ objects. instead of using 'new' i provided a function that created an object and then supplied the pointer as an output parameter. for each function you have to supply that pointer.
something like:
int fun(object* myObject, int arg1, int arg2) { myObject->fun(arg1, arg2); }
If all you want is to have a 1 on 1 mapping of functions, you could probably create a script that takes the class definitions, and then generates wrappers for it.
Of course this only works if your classes use simple datatypes for passing data because you cannot use std::string and others for passing data between your app and dll. you also have to be careful with structures, since some programming languages cannot create all types of structure.
I know that this is not what you wanted to hear, but unless your data and your classes are trivial, You have to do a lot of things by hand. Are there specific things that you still need to know?
 Signature Kind regards, Bruno. bruno_nos_pam_van_dooren@hotmail.com Remove only "_nos_pam"
Doug Harrison [MVP] - 18 Apr 2006 14:04 GMT >I have a set of C++ classes for which I want to provide a C API - and >compile into a C DLL, for use in an application that can only work with [quoted text clipped - 3 lines] >anything up that directly addresses the issue (parashift info is useful, >but does not deal specifically, with the isse) You'll have to create a set of C functions that map to C++ counterparts, and you'll have to pass the this pointer around in some way. Something like this should work:
class X { public:
X();
void f(); };
// C interface
#ifdef __cplusplus extern "C" { #endif
// Type-safe, opaque pointer. typedef struct C_X_* X_Handle;
// Export these using __declspec, for example
X_Handle CreateX(); void DestroyX(X_Handle x);
void X_f(X_Handle x);
#ifdef __cplusplus } #endif
// C++ implementation
X_Handle CreateX() { return reinterpret_cast<X_Handle>(new X); }
void DestroyX(X_Handle x) { delete reinterpret_cast<X*>(x); }
void X_f(X_Handle x) { reinterpret_cast<X*>(x)->f(); }
Just make sure you only ever store an X* in an X_Handle and vice versa. Oh, and your C++ wrapper functions should not allow exceptions to propagate out through C functions; they should return error codes instead.
 Signature Doug Harrison Visual C++ MVP
Bit byte - 18 Apr 2006 14:36 GMT >>I have a set of C++ classes for which I want to provide a C API - and >>compile into a C DLL, for use in an application that can only work with [quoted text clipped - 57 lines] > and your C++ wrapper functions should not allow exceptions to propagate out > through C functions; they should return error codes instead. Hi Doug,
Thanks for that. This is the skinda stuff I was looking for.
Npow, are there any gotchas etc w.r.t the ff:
1). virtual methods - can 'exported class ' class X (given in your example), contain virtual methods?
2). invoking methods on derived classes (can 'exposed clsss' class X have been derived from another C++ class ? ) - i.e. class X : public Y, or can you only expose a C interface to classes that are not inheriting from another class.?
3). Which of the ff do you reckon is the better approach: (i). Create a C++ DLL and then create a C DLL that uses the C++ DLL (ii). Compile the C++ classes with an exported C API in the DLL - i.e. one Dll
4). How can callback functions be implemented in the C API? suppose we have the ff:
void (CB_FUNC*)(const type1, cponst type2*);
//C++ header class X { public: X(); virtual ~X(); // <- is virtual ok ?
registerCallback(CB_FUNC, void* data); //how is this exported an used from the C API ?
};
Doug Harrison [MVP] - 19 Apr 2006 03:21 GMT >Hi Doug, > [quoted text clipped - 9 lines] >or can you only expose a C interface to classes that are not inheriting >from another class.? Neither (1) nor (2) should pose a problem as long as you "only ever store an X* in an X_Handle and vice versa". For example, if Y were derived from X, you would convert a Y* to X* before converting to X_Handle. Using X_Handle per my previous message is really just a way to get some type safety on the C side of the equation, for which it's much better than using void*. Under the hood, however, it's not very different. (Windows uses a similar technique for HWND, HFONT, etc.)
>3). Which of the ff do you reckon is the better approach: >(i). Create a C++ DLL and then create a C DLL that uses the C++ DLL >(ii). Compile the C++ classes with an exported C API in the DLL - i.e. >one Dll Hard to say. If the C API is as integral to your usage as the C++ API, I'd go with the one DLL. If you go with two DLLs, your DLL clients will be able to use either interface, which may or may not be good. Well, I guess you could choose not to supply a C++ header, which would make things more difficult for the would-be C++ user.
>4). How can callback functions be implemented in the C API? >suppose we have the ff: [quoted text clipped - 11 lines] > >}; Again, as long as you keep your types consistent across the language boundaries, "virtual" won't be a problem. As your callback takes a regular function pointer (not a pointer to member function), you could use something like this:
extern "C" void RegisterCallback(X_Handle obj, CB_FUNC cb, void* data) { // But don't propagate exceptions... ((X*) obj)->registerCallback(cb, data); }
It would get more complicated if CB_FUNC's parameters were C++ types. I'd consider hanging it up if CB_FUNC were a pointer to member.
 Signature Doug Harrison Visual C++ MVP
Sean M. DonCarlos - 19 Apr 2006 05:07 GMT You might get something close to virtual functions:
// With apologies to Doug, for borrowing his code class X { public: X();
virtual void f(); };
class Y : public X { public: Y();
virtual void f(); };
// C interface
#ifdef __cplusplus extern "C" { #endif
// Type-safe, opaque pointer. typedef struct C_X_* X_Handle; typedef struct C_Y_* Y_Handle;
// Export these using __declspec, for example
X_Handle CreateX(); void DestroyX(X_Handle x);
Y_Handle CreateY(); void DestroyY(Y_Handle y);
void X_f(X_Handle x); void Y_f(Y_Handle y);
#ifdef __cplusplus } #endif
// C++ implementation
X_Handle CreateX() { return reinterpret_cast<X_Handle>(new X); }
void DestroyX(X_Handle x) { delete reinterpret_cast<X*>(x); }
void X_f(X_Handle x) { reinterpret_cast<X*>(x)->f(); }
Y_Handle CreateY() { return reinterpret_cast<Y_Handle>(new Y); }
void DestroyY(Y_Handle y) { delete reinterpret_cast<Y*>(y); }
void Y_f(Y_Handle y) { reinterpret_cast<Y*>(y)->f(); }
So far, so good. If we have an X object, we can use the CreateX, DestroyX and X_f functions; and if we have a Y object, we can use the CreateY, DestroyY and Y_f functions. But to truly get the "virtual" part going, we would need to do something like this and have it work:
Y_Handle y = CreateY(); X_f(y); // if this is really virtual, it will call the Y object's version of f.
But here's the problem: The first parameter of X_f must be of type X_Handle (really type C_X_*). But the variable y is of type Y_Handle (or type C_Y_*). Therefore, the code X_f(y) won't compile, because the types don't match, and there is no implicit conversion from C_Y_* to C_X_*.
Something like X_f(reinterpret_cast<X_Handle>(y)) might do it, though. I'd give it a shot. Presumably the y variable, which has type C_Y_* would first be cast to type X_Handle (or C_X_*), then cast again to type X* inside the X_f function. Then the program would see a pointer to an X object try to select its member f function. Since y really points to a Y object, and f is virtual, you would theoretically get Y's version of f.
Even if it doesn't work, you'll learn something about just how far you can cast a pointer before it breaks. :)
Sean
> Hi Doug, > [quoted text clipped - 30 lines] > > }; Sean M. DonCarlos - 19 Apr 2006 05:16 GMT My apologies, I forgot what language I was in for a moment when I said:
> Something like X_f(reinterpret_cast<X_Handle>(y)) might do it, though. I'd > give it a shot. Presumably the y variable, which has type C_Y_* would first > be cast to type X_Handle (or C_X_*), then cast again to type X* inside the > X_f function. Then the program would see a pointer to an X object try to > select its member f function. Since y really points to a Y object, and f is > virtual, you would theoretically get Y's version of f. Of course, on the C side of things, you're not going to be able to use reinterpret_cast. So you should try something like this instead:
X_f((X_Handle)(y));
Sean
> You might get something close to virtual functions: > [quoted text clipped - 132 lines] > > > > }; Carl Daniel [VC++ MVP] - 19 Apr 2006 05:16 GMT > Hi Doug, > [quoted text clipped - 4 lines] > 1). virtual methods - can 'exported class ' class X (given in your > example), contain virtual methods? Yes, but you'll have to be careful in some circumstances. One thing you might consider is exposing a COM interface, which can be consumed from C as well as C++ and provides in essense a mapping of C++ virtual function dispatching to C.
> 2). invoking methods on derived classes (can 'exposed clsss' class X > have been derived from another C++ class ? ) - i.e. class X : public > Y, or can you only expose a C interface to classes that are not > inheriting from another class.? Derivation doesn't matter. What matters is how you cast the 'this' pointer. As long as there's no multiple inheritance involved, you don't really have to worry about it.
> 3). Which of the ff do you reckon is the better approach: > (i). Create a C++ DLL and then create a C DLL that uses the C++ DLL > (ii). Compile the C++ classes with an exported C API in the DLL - i.e. > one Dll Whichever is more appropriate to your project. I've used both techniques, depending on circumstances either might be better for you. If you don't have a good reason to implement two DLLs, I'd for for the single DLL solution just because it's simpler.
> 4). How can callback functions be implemented in the C API? > suppose we have the ff: [quoted text clipped - 9 lines] > registerCallback(CB_FUNC, void* data); //how is this exported > an used from the C API ? Callbacks of this type aren't really anything special, since the callback is an ordinary function pointer.
you'd export the callback registration function using something along the lines of:
registerCallback(X_Handle, CB_FUNC, void*);
HTH
-cd
Bit byte - 21 Apr 2006 02:34 GMT >>I have a set of C++ classes for which I want to provide a C API - and >>compile into a C DLL, for use in an application that can only work with [quoted text clipped - 57 lines] > and your C++ wrapper functions should not allow exceptions to propagate out > through C functions; they should return error codes instead. Hi Doug,
Just needed to clarify - where does one actually define struct C_X ?
I mean do I need to create a similar structure in the C code (similar to class X?).
I mean I can't see how we can enforce type safety of the pointers since struct C_X is not defined anywhere. Am I missing something?
Please explain
Doug Harrison [MVP] - 21 Apr 2006 02:52 GMT >Hi Doug, > [quoted text clipped - 7 lines] > >Please explain No, you shouldn't try to mimic the C++ type in C. The idea is for the C side of things to work only with the opaque pointer type X_Handle, which is declared like this:
typedef struct C_X_* X_Handle;
The struct C_X_ is an incomplete type that is never completed. You can declare such a type inline in the typedef, like I did, and it's equivalent to:
struct C_X_; typedef struct C_X_* X_Handle;
You can't even dereference an X_Handle; about all you can do is copy it, such as when you pass it to functions, and take its address. Think of X_Handle as analogous to HWND, HDC, etc, and you've got it. The typedef X_Handle is safer than using void*, since each C++ type exposed to C in this way will have its own personal handle type. The name C_X_ is arbitrary; Windows does this so much, MS created a macro to ease the declaration of handles, and IIRC, it's called DECLARE_HANDLE.
 Signature Doug Harrison Visual C++ MVP
Bit byte - 21 Apr 2006 03:22 GMT >>Hi Doug, >> [quoted text clipped - 28 lines] > arbitrary; Windows does this so much, MS created a macro to ease the > declaration of handles, and IIRC, it's called DECLARE_HANDLE. Thanks Doug, you've been a real help. I can finally get to doing some coding now...
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|