I'm looking to develop a set of C APIs that will wrap around our existing C++ APIs to access our core logic (written in object-oriented C++). This will essentially be a glue API that allows our C++ logic to be usable by other languages. What are some good tutorials, books, or best-practices that introduce the concepts involved in wrapping C around object-oriented C++?
I would think you may be able to get some ideas on direction and/or possibly utilize directly SWIG. I would think that going over a few of the examples would at least give you an idea of what kinds of things to consider when wrapping one API into another. The exercise could be beneficial.
SWIG is a software development tool that connects programs written in C and C++ with a variety of high-level programming languages. SWIG is used with different types of languages including common scripting languages such as Perl, PHP, Python, Tcl and Ruby. The list of supported languages also includes non-scripting languages such as C#, Common Lisp (CLISP, Allegro CL, CFFI, UFFI), Java, Lua, Modula-3, OCAML, Octave and R. Also several interpreted and compiled Scheme implementations (Guile, MzScheme, Chicken) are supported. SWIG is most commonly used to create high-level interpreted or compiled programming environments, user interfaces, and as a tool for testing and prototyping C/C++ software. SWIG can also export its parse tree in the form of XML and Lisp s-expressions. SWIG may be freely used, distributed, and modified for commercial and non-commercial use.
Just replace the concept of an object with a void *
(often referred to as an opaque type in C oriented libraries) and reuse everything you know from C++.
It is not hard to expose C++ code to C, just use the Facade design pattern
I am assuming your C++ code is built into a library, all you need to do is make one C module in your C++ library as a Facade to your library along with a pure C header file. The C module will call the relevant C++ functions
Once you do that your C applications and library will have full access to the C api you exposed.
for example, here is a sample Facade module
#include <libInterface.h>
#include <objectedOrientedCppStuff.h>
int doObjectOrientedStuff(int *arg1, int arg2, char *arg3) {
Object obj = ObjectFactory->makeCppObj(arg3); // doing object oriented stuff here
obj->doStuff(arg2);
return obj->doMoreStuff(arg1);
}
you then expose this C function as your API and you can use it freely as a C lib with out worrying about
// file name "libIntrface.h"
extern int doObjectOrientedStuff(int *, int, char*);
Obviously this is a contrived example but this is the easiest way to expos a C++ library to C
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
- Every object is passed about in C an opaque handle.
- Constructors and destructors are wrapped in pure functions
- Member functions are pure functions.
- Other builtins are mapped to C equivalents where possible.
So a class like this (C++ header)
class MyClass
{
public:
explicit MyStruct( std::string & s );
~MyStruct();
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"
struct HMyClass : public MyClass {};
extern "C"
{
HMyClass * myStruct_create( const char * s )
{
return new HMyClass( s );
}
void myStruct_destroy( HMyClass * v )
{
delete v;
}
int myStruct_doSomething( HMyClass * v, int i )
{
return v->doSomething(i);
}
}
We derive our opaque handle from the original class to avoid needing any casting, and we have to make the handle a struct as C doesn't support classes.
So that gives us the C interface. The fun part is now ensuring that you get all the required C++ libraries linked into you larger library correctly.