Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsFree MagazinesWhite PapersSubmit Content
Discussion GroupsASP.NETWindows FormsLanguages.NET FrameworkVisual Studio.NET
Articles.NET FrameworkASP.NETToolsWindows Forms
.NET DirectoryOpen Source ProjectsUser GroupsWeb Resources
Related Topics
Visual Basic 6SQL ServerMS AccessOther DB ProductsMS Server ProductsMore Topics ...

.NET Forum / Languages / Managed C++ / April 2006

Tip: Looking for answers? Try searching our database.

C interface to C++ classes

Thread view: 
Enable EMail Alerts  Start New Thread
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 Magazines

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

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.