views:

280

answers:

9

When preparing a library (let's call it libfoo), I find myself presented with the following dilemma: do I write it as a C++ library with a C wrapper:

namespace Foo {
  class Bar {
    ...
  };
}

/* Separate C header. #ifdef __cplusplus omitted for brevity. */
extern "C" {
  typedef void *FooBar;
  FooBar* foo_bar_new() { return new Foo::Bar; }
  void foo_bar_delete(FooBar *bar) { delete bar; }
}

Or is it better to write it as a C library with a C++ wrapper:

/* foo/bar.h. Again, #ifdef __cplusplus stuff omitted. */

typedef struct {
  /* ... */
} FooBar;

void foo_bar_init(FooBar *self) { /* ... */ }
void foo_bar_deinit(FooBar *self) { /* ... */ }

/* foo/bar.hpp */

namespace Foo {
  class Bar {
    /* ... */
    FooBar self;
  }

  Bar::Bar() {
    foo_bar_init(&self);
  }

  Bar::~Bar() {
    foo_bar_deinit(&self);
  }
}

Which do you prefer, and why? I favour the latter because it means I don't have to worry about my C functions accidentally having exceptions bubble up, plus I prefer C as a language as I feel that it's a smaller semantic minefield. What do other people think?

EDIT: So many good answers. Thanks all. It's a shame that I can only accept one.

+7  A: 

Write the library in the language you prefer to write libraries in. It doesn't technically much matter which way you wrap. Although some C projects may aim to exclude libraries that aren't C whereas it'd be odd for a C++ project to exclude libraries written in C, that's mostly a philosophical objection than a practical one.

Wrapping C in a C++ wrapper will likely result in a slightly larger wrapper but be more acceptable to C programmers.

Note that if you are distributing binaries, C's simplicity is advantageous.

Eamon Nerbonne
+1 for "Write the library in the language you prefer to write libraries in"
JeremyP
I'm aware of many companies that refuse to use C++ libs such as Qt or Boost from C++.
Matt Joiner
+4  A: 

If you prefer to write in C, why do you need C++ wrapper? C++ client can use C-style API interface. On the other hand, you you prefer C++, it is necessary to have C wrapper for C clients.

Alex Farber
To work reasonably in C++, at a minimum, you want to support RAII - otherwise any C++ consumer will need to write such a wrapper themselves for anything but the simplest of cases. Also, even if you don't, you'll need a header that's valid C++ (not hard), and includes extern "C" linkage - you can't just include C header file and link to C otherwise.
Eamon Nerbonne
Agree that C library public h-file must have extern "C" definition for C++ clients. Regarding RAII - I think that this is an option. But if there is C++ wrapper that implements this - it is better.
Alex Farber
@Eamon Nerbonne: In many cases clients can add RAII externally, and adding that to the library will only force your RAII solution on their code. It is quite simple to use `shared_ptr` to wrap a library with `create/deleter` function pairs by passing the `deleter` function to the `shared_ptr` constructor. I know that there are other cases, but that depends more on the domain.
David Rodríguez - dribeas
@Alex: As a convenience to library users. This isn't so strange, libev does this, for instance.
Jack Kelly
+2  A: 

I personally prefer to use C++ and would wrap it to C. But infact it's a matter of taste and you'd have to make your own decision how you'd like it. If you feel more comfortable writing the library in C then go for it and wrap it for C++.

About the exceptions: You can catch them in every function wrapped for C and return an error code for them, by e.g. having an own exception class which has already a numeric error code value which you may return to your C functions, others which might have been thrown by any other libraries can be translated to something else, however you should have caught them earlier anyway.

Vinzenz
+2  A: 

If you feel comfortable with writing your library in C then do it. It will be more portable as a C library and has no issues with exceptions as you mentioned. It is uncommon to start with a C++ library and wrap it in C.

frast
+4  A: 

If your lib will ever have to be distributed as a binary + header (instead of shipping the source code), you will find that a C API is more universally linkable, since C usually is the smallest common API on any platform.

This is why I usually had to make C APIs with inline C++ wrappers around them for the projects I did in the last decade that needed an API. Since the programs were all in C++, this meant I had to make a C wrapper API around C++ code, just to put another wrapping C++ API around it.

sbi
Right, but the *API* would be C anyhow, regardless of the implementation language.
Eamon Nerbonne
@Earmon: I'm not sure whether your comment disagrees with anything I wrote and, if so, with what.
sbi
So if you are able to write the implementation in C you have only one wrapper instead of two.
frast
@sbi - not disagreeing, merely noting that the API being C doesn't mean the lib has to be - you emphasized the C API, which could be confusing considering the question focuses on the implementation. Incidentally, if you implement the lib in C++, why wrap it twice to produce the API? Why not just export the relevant symbols directly?
Eamon Nerbonne
@frast: Yes, but writing a C wrapper around a non-too-fancy C++ API is tedious at worst, but not hard. If you're more comfortable with C++, writing a whole lib in C might be much worse than writing a C wrapper API around a C++ lib.
sbi
@Earmon: Maybe it didn't com across that way, but what I was trying to say was that _underneath_ should be a C API, because usually that can be linked to from just about every language/tool available on a given platform. The C++ API should be a small inline wrapper on top of that. What is _inside the lib_ doesn't really matter for that. As I wrote, I have, in the past, wrapped C++ libs with a C APIs and wrapped C++ APIs around that. (Hard to wrap your head around that.)
sbi
@sbi: I agree. If the C++ API already exists or you have to use C++ libraries then a C wrapper is more reasonable.
frast
Out of curiosity, why did you wrap C++ around C around C++ instead of just calling the originals directly?
Eamon Nerbonne
+1 for the idea of making the wrappers `inline`.
Jack Kelly
@Earmon: Because we had C++ code and on all platforms for which I had to make these APIs (Win32, OSX, Linux, BSD, Solaris...) there is a universal C ABI, but no universal C++ ABI. Crossing the boundaries between binaries had to be done using a C API, which is why we had an underlying C API. But the support costs for the C API became a real problem, because developers using it would do stupid things and then call us to find out about that. So I finally got the time assigned to wrap a nice, clean, easy-to-use C++ API around that, which helped tremendously.
sbi
@sbi: An amusing situation to find yourself in :D
Matt Joiner
+2  A: 

Assuming compilation without link-time optimizations, a C compiler can't inline the wrapper functions as it doesn't know how to handle C++ calls - but a C++ compiler can easily inline C calls.

Therefore, it might be a good idea to create a C++ wrapper for the C library, instead of the other way around.

Christoph
Assuming you're not including function bodies in the headers (which you generally won't be), neither can be inlined without link-time optimizations.
Eamon Nerbonne
@Eamon: your assumption is flawed - wrapper functions are a case where it's normally acceptable to include function bodies in header files
Christoph
Wrapper functions around *what*? If the wrapper functions themselves merely contain calls to the library implementation, *those* won't be inlineable. Whether the lib is C++ or C, there will always be a gap that makes inlining impossible. If calling C from C++, before the language transition on the C++ side of the fence the wrapper but not the implementation can be inlined, and if calling C++ from C, then after the language transition the implementation but not the wrapper can be inlined... Without LTO, you can't avoid the function call.
Eamon Nerbonne
@Eamon: case 1: `cxx_wrapper_func()` calls `c_api_func()`; you can put the definition of `cxx_wrapper_func()` into a header file and include it into any `cxx` file, which allows the C++ compiler to inline `cxx_wrappe_func()`, getting rid of the wrapper function overhead; case 2: `c_wrapper_func()` calls `cxx_api_func()`; because a C compiler won't understand the C++ calling convention, you have to compile `c_wrapper_func()` with a C++ compiler, ie you'll have to put the definition in its own `cxx` file, making inlining from C impossible
Christoph
Right, so in the first case, there is exactly 1 absolutely required function call, namely to `c_api_func`. And in the second case, there is exactly... 1 absolutely required function call, namely to `c_wrapper_func`. After all, `c_wrapper_func` *is* compiled by the C++ compiler and *can* inline `cxx_api_func`. In any case - you'll have function calls, you won't have inter-module optimization, and the resulting code will be ill-suited for performance-critical tiny functions - for which you generally want a header-lib anyhow. But the direction of wrapping doesn't (much) matter.
Eamon Nerbonne
@Eamon: you're right - I missed that `cxx_api_func()` and `c_wrapper_func()` might reside in the same translation unit, because that's not how it was done the last time I worked on something like this (they had a seperate `capi.cxx` file); there are still some problems with this approach, though: inlining `cxx_api_func()` possibly duplicates a lot of code and might not be feasible
Christoph
Yeah, true enough - the code size issue is potentially quite relevant.
Eamon Nerbonne
Huh? Why isn't the linker optimizing functions from other object files? I thort this was common practise?
Matt Joiner
@Matt: historically, the linker only performs very basic optimizations (it couldn't do much as most of the information from the compilation phase had been lost); advanced LTO were not present in gcc until October 2009 (see http://gcc.gnu.org/ml/gcc/2009-10/msg00060.html )
Christoph
@Christoph: Thanks! I had no idea.
Matt Joiner
+8  A: 

Small points:

When you write C library it is useful anywhere - in C, in C++ (with wrapper) and many other languages like Python, Java using bindings etc and most important it requires only C runtime.

When you write C++ wrapper you also need to write a C wrapper, but it is not as simple as you think, for example:

c_api.h:

extern "C" {
  typedef void *Foo;
  Foo create_foo();
}

c_api.cpp:

void *create_foo() 
{
    return new foo::Foo();
}

What is wrong? it may throw! and the program will crash as C does not have stack unwinding semantics. So you need something like:

void *create_foo() 
{
    try {
       return new foo::Foo();
    }
    catch(...) { return 0; }
}

And this for every C++ api function.

So I think that writing a C library and providing a separate C++ wrapper is better solution.

Also it would not require linking with C++ runtime library.

Artyom
Another small point: On some platforms with multiple compiler vendors (e.g. Windows), the C ABI is extremely stable and works cross vendor. The C++ ABI tends to be highly compiler vendor specific, and in some cases getting a binary of a library built with one C++ compiler to work with a client application built with a second C++ compiler is an intractable problem. See [this MinGW wiki article](http://www.mingw.org/wiki/MixingCompilers) for one point of view.
RBerteig
Also note that C++ is a really painful language. Make C++ wrappers for the bozos that can't do it themselves (this is really bad practise in my opinion). C bindings are a must.
Matt Joiner
+2  A: 

It also depends a lot on what you plan to use in your library. If it in turn could benefit greatly from other C++ libraries, then use C++.

It could also be argued, that if your library is going to be very big (internally, not necessarily API wise) it can be easier to implement it in C++. (It is not my cup of tea, I prefer C, but some people swear by C++.)

Also keep in mind, that C++ uses a runtime that pretty much demands an operating system, for exception support.

If you envision your library to be used as a foundation for an operating system, or to be used in environments without an operating system you either have to know how to disable exception support, avoiding a lot (all?) of STL and provide your own allocator and deallocator. It's not impossible, but you need to know exactly what you do.

C is more suited to those low level kinds of things.

Amigable Clark Kant
+1  A: 

Personally I prefer to write it in C++, then expose the C interface using a wrapper. Mostly because I'd rather write in a proper OO language. I'd use an OO style C wrapper too, liek I outline in this post I wrote a pretty detailed explanation about what you need to d to call OO C++ from C in this post http://stackoverflow.com/questions/2045774/developing-c-wrapper-api-for-object-oriented-c-code/2045860#2045860

Michael Anderson