Developing C Wrapper API For Object-Oriented C++ Code

Developing C wrapper API for Object-Oriented C++ code

This is not too hard to do by hand, but will depend on the size of your interface.
The cases where I've done it were to enable use of our C++ library from within pure C code, and thus SWIG was not much help. (Well maybe SWIG can be used to do this, but I'm no SWIG guru and it seemed non-trivial)

All we ended up doing was:

  1. Every object is passed about in C an opaque handle.
  2. Constructors and destructors are wrapped in pure functions
  3. Member functions are pure functions.
  4. Other builtins are mapped to C equivalents where possible.

So a class like this (C++ header)

class MyClass
{
public:
explicit MyClass( std::string & s );
~MyClass();
int doSomething( int j );
}

Would map to a C interface like this (C header):

struct HMyClass; // An opaque type that we'll use as a handle
typedef struct HMyClass HMyClass;
HMyClass * myStruct_create( const char * s );
void myStruct_destroy( HMyClass * v );
int myStruct_doSomething( HMyClass * v, int i );

The implementation of the interface would look like this (C++ source)

#include "MyClass.h"

extern "C"
{
HMyClass * myStruct_create( const char * s )
{
return reinterpret_cast<HMyClass*>( new MyClass( s ) );
}
void myStruct_destroy( HMyClass * v )
{
delete reinterpret_cast<MyClass*>(v);
}
int myStruct_doSomething( HMyClass * v, int i )
{
return reinterpret_cast<MyClass*>(v)->doSomething(i);
}
}

We derive our opaque handle from the original class to avoid needing any casting, and (This didn't seem to work with my current compiler). We have to make the handle a struct as C doesn't support classes.

So that gives us the basic C interface. If you want a more complete example showing one way that you can integrate exception handling, then you can try my code on github : https://gist.github.com/mikeando/5394166

The fun part is now ensuring that you get all the required C++ libraries linked into you larger library correctly. For gcc (or clang) that means just doing the final link stage using g++.

Wrapping C++ class API for C consumption

Foreach public method you need a C function.

You also need an opaque pointer to represent your class in the C code.

It is simpler to just use a void* though you could build a struct that contains a void* and other information (For example if you wanted to support arrays?).

Fred.h
--------------------------------

#ifdef __cplusplus
class Fred
{
public:
Fred(int x,int y);
int doStuff(int p);
};
#endif

//
// C Interface.
typedef void* CFred;

//
// Need an explicit constructor and destructor.
extern "C" CFred newCFred(int x,int y);
extern "C" void delCFred(CFred);

//
// Each public method. Takes an opaque reference to the object
// that was returned from the above constructor plus the methods parameters.
extern "C" int doStuffCFred(CFred,int p);

The the implementation is trivial.

Convert the opaque pointer to a Fred and then call the method.

CFred.cpp
--------------------------------

// Functions implemented in a cpp file.
// But note that they were declared above as extern "C" this gives them
// C linkage and thus are available from a C lib.
CFred newCFred(int x,int y)
{
return reinterpret_cast<void*>(new Fred(x,y));
}

void delCFred(CFred fred)
{
delete reinterpret_cast<Fred*>(fred);
}

int doStuffCFred(CFred fred,int p)
{
return reinterpret_cast<Fred*>(fred)->doStuff(p);
}

C wrapper for C++ library - what about inheritance?

There isn't any perfect approach. You're trying to make your function take some opaque handles (the ones with the appropriate base class) but not any handle type (which void* would accept), and there just isn't a thing in C for that.

If you like, you can provide a function which takes serial_channel* and returns channel*, and another one for each other channel subclass. This gets you away from unsafe C casting, and doesn't require numfuncs*numderivedclasses different channel-taking functions.

Personally, I'd just void* it. They're using C, after all... clearly they don't care too much about their language keeping them safe.

Creating Object-Oriented Wrappers for the WinAPI

Try http://vaca.sourceforge.net/

Create a C wrapper around a C++ library that can be linked by a C linker

This link explains some of the compiler options and scenarios: http://docs.oracle.com/cd/E19422-01/819-3690/Building.Libs.html Specifically:

> 16.7 Building a Library That Has a C API

If you want to build a library that is written in C++ but that can be
used with a C program, you must create a C API (application
programming interface). To do this, make all the exported functions
extern "C". Note that this can be done only for global functions and
not for member functions.


If a C-interface library needs C++ run-time support and you are
linking with cc, then you must also link your application with either
libC (compatibility mode) or libCrun (standard mode) when you use the
C-interface library. (If the C-interface library does not need C++
run-time support, then you do not have to link with libC or libCrun.)
The steps for linking differ for archived and shared libraries.


When providing an archived C-interface library, you must provide
instructions on how to use the library.


If the C-interface library was built with CC in standard mode (the
default), add -lCrun to the cc command line when using the C-interface
library. If the C-interface library was built with CC in compatibility
mode (-compat), add -lC to the cc command line when using the
C-interface library. When providing a shared C-interface library you
must create a dependency on libC or libCrun at the time that you build
the library. When the shared library has the correct dependency, you
do not need to add -lC or -lCrun to the command line when you use the
library.


If you are building the C-interface library in compatibility mode
(-compat), add -lC to the CC command line when you build the library.
If you are building the C-interface library in standard mode (the
default), add -lCrun to the CC command line when you build the
library. If you want to remove any dependency on the C++ runtime
libraries, you should enforce the following coding rules in your
library sources:


Do not use any form of new or delete unless you provide your own
corresponding versions. Do not use exceptions. Do not use runtime type
information (RTTI).

How to declare a pointer to a nested C++ class in C

It's not a problem per-se, but you might violate the strict aliasing rule if and when you cast that void * back to Mgr::Obj * in your C++ code.

There's no good solution to this - it is (to me) a glaring omission in the standard. The best you can do is to compile any code which does such casts with the -fno-strict-aliasing flag. I do this extensively and have never had a problem performing casts in this manner.



Related Topics



Leave a reply



Submit