473,388 Members | 1,355 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,388 software developers and data experts.

C interface to C++ classes

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)

Apr 18 '06 #1
10 1545
> 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)


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?

--

Kind regards,
Bruno.
br**********************@hotmail.com
Remove only "_nos_pam"

Apr 18 '06 #2
On Tue, 18 Apr 2006 12:55:21 +0100, Bit byte <fl**@flop.com> wrote:
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)


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.

--
Doug Harrison
Visual C++ MVP
Apr 18 '06 #3


Doug Harrison [MVP] wrote:
On Tue, 18 Apr 2006 12:55:21 +0100, Bit byte <fl**@flop.com> wrote:

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)

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.


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 ?

};

Apr 18 '06 #4
On Tue, 18 Apr 2006 14:36:54 +0100, Bit byte <fl**@flop.com> wrote:
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.?
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:

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 ?

};


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.

--
Doug Harrison
Visual C++ MVP
Apr 19 '06 #5
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

"Bit byte" wrote:
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 ?

};

Apr 19 '06 #6
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

"Sean M. DonCarlos" wrote:
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

"Bit byte" wrote:
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 ?

};

Apr 19 '06 #7
Bit byte wrote:
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?
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:

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 ?


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
Apr 19 '06 #8


Doug Harrison [MVP] wrote:
On Tue, 18 Apr 2006 12:55:21 +0100, Bit byte <fl**@flop.com> wrote:

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)

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.


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

Apr 21 '06 #9
On Fri, 21 Apr 2006 02:34:51 +0100, Bit byte <fl**@flop.com> wrote:
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


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.

--
Doug Harrison
Visual C++ MVP
Apr 21 '06 #10


Doug Harrison [MVP] wrote:
On Fri, 21 Apr 2006 02:34:51 +0100, Bit byte <fl**@flop.com> wrote:

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

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.


Thanks Doug, you've been a real help. I can finally get to doing some
coding now...

Apr 21 '06 #11

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

9
by: Anon Email | last post by:
Hi people, I'm learning about header files in C++. The following is code from Bartosz Milewski: // Code const int maxStack = 16; class IStack
26
by: Marius Horak | last post by:
As in subject. Thanks MH
20
by: Ole Hanson | last post by:
I am accessing my database through an interface, to allow future substitution of the physical datastore - hence I would like to declare in my Interface that my DAL-objects implementing the...
9
by: phl | last post by:
hi, I am kind of confused aobut interfaces and abstract classes. In short as I understand it, an interface is like a contract between the class and the interface, so that certain funtions must...
10
by: Brett | last post by:
I'm still trying to figure out concrete reasons to use one over the other. I understand the abstract class can have implementation in its methods and derived classes can only inherit one abstract...
7
by: Brett | last post by:
I have created an interface with five properties and five methods. Five classes are accessed through this interface and contain the implementation. This setup works fine now. All the classes are...
15
by: mr.peteryu | last post by:
Hi, Can someone explain the idea behind casting to an interface? For example: -> I have an IInterface that contains a Read() method. -> I have an object "obj" that implements IInterface. ...
20
by: Luc Kumps | last post by:
(Sorry about the previous post, it got transmitted before it was complete) We try to separate implementation and interface defintions, but we run into a problem. I hope the guru's can solve this,...
15
by: Xah Lee | last post by:
On Java's Interface Xah Lee, 20050223 In Java the language, there's this a keyword “interface”. In a functional language, a function can be specified by its name and parameter specs....
9
by: msbs1984 | last post by:
Difference Between Interface and Abstract Class?
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.