96

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

theactiveactor
  • 7,314
  • 15
  • 52
  • 60
  • 5
    check out the zeromq source for inspiration. The library is currently written in C++, and has C bindings. http://www.zeromq.org/ – Hassan Syed Jan 12 '10 at 19:26
  • 1
    Related (or even a duplicate): [Wrapping C++ class API for C consumption](https://stackoverflow.com/questions/1588788/wrapping-c-class-api-for-c-consumption) – user Dec 17 '14 at 09:32

6 Answers6

81

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

joel
  • 6,359
  • 2
  • 30
  • 55
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
  • 11
    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 Jan 12 '10 at 04:21
  • 3
    I agree with Laserallan and have refactored my code accordingly – Michael Anderson Jan 12 '10 at 05:00
  • 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 Jan 17 '10 at 07:55
  • 3
    @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 Jan 17 '10 at 08:05
  • 2
    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 Jan 17 '10 at 08:06
  • Why bother with inheritance? I've had no probably with the same pattern except using "MyClass" rather than a descendant. Also, I would probably recommend making your Handle type a pointer so the fact that the handle is in fact a pointer isn't explicit in return types and arguments. – N8allan Jul 27 '12 at 10:01
  • 1
    @N8allan The main reason for the dummy inheritance is to get around some annoying compiler messages about object declarations not matching definitions when using `class MyClass`. You can get around this by using a `struct MyClass` - but often `struct` implies semantically a simpler structure than `class` in C++ code. There may be a better way though. – Michael Anderson Jul 27 '12 at 11:23
  • @N8allan Thats also the easiest way to get around the compiler messages if you don't have the ability to change the class you are wrapping to a struct. – Michael Anderson Jul 27 '12 at 12:00
  • Two questions on this: 1) Should this be updated to include the exception handling code from comment by @Terry ? 2) How is the HMyClass type usable via the C compiler? Isn't the name on the implementation side mangled by C++? – nonot1 Apr 15 '13 at 21:44
  • @nonot1 1. This certainly could be augmented with some nicer exception handling, however that does add complexity to the code. Terry's `catch(...) { std::terminate(); }` would be brutal but acceptable. I'd prefer to add something a little neater - I'll think about how I can add that without muddying the code. – Michael Anderson Apr 16 '13 at 02:06
  • @nonot1 2. I mention that those definitions need to go into a C header. This means I expect the user to provide the usual header guards and bits to let C++ compilers using C headers work. (This is usually just wrapping a `extern "C" {` in some `#ifdefs`. I guess I can update that when I add the exception handling. Or alternatively the code using the header should wrap the `#include` in an `#extern "C"` block. (Different C libraries use different approaches...) – Michael Anderson Apr 16 '13 at 02:08
  • 1
    I've implemented a version with one form of exception handling, where all functions require an exception handler struct as an additional call, which gets used in the case of an exception. However its a bit too long to add here, so I've put it up on github : https://gist.github.com/mikeando/5394166 – Michael Anderson Apr 16 '13 at 08:12
  • @MichaelAnderson Understood. Thanks for clarifying. Though, why is the `reinterpret_cast<>` suddenly needed? Isn't a MyClass and/or derived HMyClass pointer exactly what's being passed in? – nonot1 Apr 16 '13 at 19:51
  • @nonot1 I had to break the inheritance between MyClass and HMyClass as it led to some compile issues. There is probably a way to avoid the cast, but for now it seemed the easiest solution. – Michael Anderson Apr 17 '13 at 00:15
  • 5
    @MichaelAnderson, there are two typos in your `myStruct_destroy` and `myStruct_doSomething` functions. Should be `reinterpret_cast(v)`. – firegurafiku Jun 15 '14 at 05:47
  • Why do you do a `reinterpret_cast(v)` before calling `doSomething`? Isn't `v` already a `MyClass*` according to the functions argument list `int myStruct_doSomething( HMyClass * v, int i )` ? – Max Jul 15 '16 at 02:51
  • @Max the argument is a `HMyClass` which is the C handle type, which needs to be cast to the C++ type `MyClass` before the C++ functions are used. – Michael Anderson Jul 15 '16 at 03:13
  • Wouldn't it be helpful to `reinterpret_cast` to a reference rather than a pointer at times, such as in your doSomething? It should be noted that this can't be done for things like con/destructors. It may also be worth noting that namespaces aren't present in C, so it's often good to put a module name in front of the typedefs and functions as well. – Daniel Underwood Sep 13 '16 at 22:33
  • @danielu13 I'm not sure that `return reinterpret_cast(v)->doSomething(i);` is much worse than `return reinterpret_cast(*v).doSomething(i);` I think they both explode in equally fun ways if passed null or invalid pointers. Or were you thinking of some other advantage? – Michael Anderson Nov 09 '16 at 01:23
  • the interface file doesn't have an include for the C++ header, but casts wrt `MyClass`. Is that right? – joel Jun 23 '21 at 15:56
  • @joel The implementation of the interface is C++ code (.cpp) and does `#include` both the C and C++ headers. Neither of the C or C++ headers `#include` the other. – Michael Anderson Jun 24 '21 at 03:51
  • sorry, i meant the implementation of the interface, which only has `#include "MyClass.h"` – joel Jun 24 '21 at 11:11
18

I think Michael Anderson's answer is on the right track but my approach would be different. You have to worry about one extra thing: Exceptions. Exceptions are not part of the C ABI so you cannot let Exceptions ever be thrown past the C++ code. So your header is going to look like this:

#ifdef __cplusplus
extern "C"
{
#endif
    void * myStruct_create( const char * s );
    void myStruct_destroy( void * v );
    int myStruct_doSomething( void * v, int i );
#ifdef __cplusplus
}
#endif

And your wrapper's .cpp file will look like this:

void * myStruct_create( const char * s ) {
    MyStruct * ms = NULL;
    try { /* The constructor for std::string may throw */
        ms = new MyStruct(s);
    } catch (...) {}
    return static_cast<void*>( ms );
}

void myStruct_destroy( void * v ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    delete ms;
}

int myStruct_doSomething( void * v, int i ) {
    MyStruct * ms = static_cast<MyStruct*>(v);
    int ret_value = -1; /* Assuming that a negative value means error */
    try {
        ret_value = ms->doSomething(i);
    } catch (...) {}
    return ret_value;
}

Even better: If you know that all you need as a single instance of MyStruct, don't take the risk of dealing with void pointers being passed to your API. Do something like this instead:

static MyStruct * _ms = NULL;

int myStruct_create( const char * s ) {
    int ret_value = -1; /* error */
    try { /* The constructor for std::string may throw */
        _ms = new MyStruct(s);
        ret_value = 0; /* success */
    } catch (...) {}
    return ret_value;
}

void myStruct_destroy() {
    if (_ms != NULL) {
        delete _ms;
    }
}

int myStruct_doSomething( int i ) {
    int ret_value = -1; /* Assuming that a negative value means error */
    if (_ms != NULL) {
        try {
            ret_value = _ms->doSomething(i);
        } catch (...) {}
    }
    return ret_value;
}

This API is a lot safer.

But, as Michael mentioned, linking may get pretty tricky.

Hope this helps

lolando
  • 1,721
  • 1
  • 18
  • 22
figurassa
  • 2,037
  • 2
  • 13
  • 12
  • 2
    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 Jan 12 '10 at 04:13
  • 2
    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 Jan 13 '10 at 06:59
  • 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 Jan 13 '10 at 07:06
  • @Emile Cormier : Yes, the idea of returning an specific error code based on the exception that was thrown is definitely a good one. – figurassa Jan 13 '10 at 13:46
  • 2
    catch(...){ } is pure unadulterated evil. My only regret is that I can only down vote once. – Terry Mahaffey Jan 17 '10 at 08:13
  • 2
    @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 Jan 17 '10 at 20:11
  • 1
    catch(...) { std::terminate(); } is acceptable. catch(...){ } is a potential security hole – Terry Mahaffey Jan 17 '10 at 20:15
  • `extern "C"` can't be under `#ifdef __cplusplus` since you will have linking issues caused by name mangling used by C++ (when `extern "C"` is not used). – Marek R Nov 09 '21 at 11:09
12

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
  • 38,949
  • 39
  • 113
  • 143
6

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
  • 13,006
  • 17
  • 55
  • 87
  • 3
    SWIG is just over kill, if all he wants to do is to make a C++ library be available from C. – hhafez Jan 12 '10 at 00:10
  • 2
    Thats an opinion and contains no real useful feedback. SWIG would help if the original code is: Changing rapidly, There are no C++ resources to maintain it and only C resources available and if the developer wants to automate the C API generation. These are common and certainly valid reasons to use SWIG. –  Sep 07 '18 at 08:28
5

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
  • 20,075
  • 11
  • 87
  • 171
3

I think using SWIG is the best answer... not only it avoid reinventing wheel but it is reliable and also promote a continuity in development rather than one shooting the problem.

High frequency problems need to be addressed by a long term solution.

danbo
  • 147
  • 7