tags:

views:

516

answers:

5

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++?

+2  A: 

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.

harschware
SWIG is just over kill, if all he wants to do is to make a C++ library be available from C.
hhafez
+2  A: 

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

Hassan Syed
+2  A: 

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

hhafez
+11  A: 

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

Michael Anderson
I'd recommend you to use something else than void, for instance an anonymous struct instead of a void* for the returned object. This can give some kind of type safety for the returned handles.Check out http://stackoverflow.com/questions/839765/the-right-type-for-handles-in-c-interfaces for more info about it.
Laserallan
I agree with Laserallan and have refactored my code accordingly
Michael Anderson
Surely it's not valid to use new/delete inside an extern "C" block? Or does that just affect the function name mangling?
Mike Weller
Also see http://www.parashift.com/c++-faq-lite/mixing-c-and-cpp.html
Mike Weller
@Mike Weller The new and delete inside the extern "C" block is fine. The extern "C" does only effect the name mangling. The C compiler never sees that file, only the header.
Michael Anderson
I also missed a typedef needed to make it all compile in C. The weird typdef struct Foo Foo; "hack". Code is updated
Michael Anderson
+2  A: 
figurassa
For more about exception handling for this case take a look at the following thread:http://stackoverflow.com/questions/847279/code-reuse-in-exception-handling
Laserallan
@Laserallan : Thx... good link
figurassa
When I know my C++ library will have also have a C API, I encapsulate an API error code int inside my exception base class. It is easier to know at the throwing site what the exact error condition is and provide a very-specific error code. The try-catch "wrappers" in the outer C API functions simply need to retrieve the error code and return it to the caller.For other standard library exceptions, refer to Laserallan's link.
Emile Cormier
I wish that all standard library exceptions had a standard error code (perhaps matching posix errno codes) embedded in them for just this purpose.
Emile Cormier
@Emile Cormier : Yes, the idea of returning an specific error code based on the exception that was thrown is definitely a good one.
figurassa
catch(...){ } is pure unadulterated evil. My only regret is that I can only down vote once.
Terry Mahaffey
@Terry Mahaffey I absolutely agree with you that it is evil. The best is to do what Emile has suggested. But if you must guarantee that the wrapped code will never throw, you have no other choice but to put a catch (...) at the bottom of all the other catches identified. This is the case because the library you are wrapping may be poorly documented. There are no C++ constructs that you can use in order to enforce that only a set of exceptions may be thrown. What is the lesser of two evils? catch (...) or risking a run-time crash when the wrapped code attempts to throw to the C caller?
figurassa
catch(...) { std::terminate(); } is acceptable. catch(...){ } is a potential security hole
Terry Mahaffey