18

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.

Jack Kelly
  • 18,264
  • 2
  • 56
  • 81

9 Answers9

22

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
  • 31,019
  • 21
  • 127
  • 215
  • 7
    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 Oct 12 '10 at 08:05
  • 2
    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 Oct 13 '10 at 13:40
  • 2
    @MattJoiner `Also note that C++ is a really painful language. Make C++ wrappers for the bozos that can't do it themselves` was posted in Oct 2010 and now in July 2012 still no one has taken the flame bait? I'm so proud of you StackOverflow. *tear*. – cheshirekow Jul 20 '12 at 18:47
  • 1
    Mostly I agree with this answer, but you can also simply choose to not use exceptions in your c++ implementation. Or, you can provide an additional layer of c++ that handles exceptions, the backend of which can be used by the c-wrapper. That said, a C library will likely get around more than a C++ library because other languages can link against it without as much trouble. – cheshirekow Jul 20 '12 at 18:52
9

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
  • 47,023
  • 20
  • 101
  • 166
5

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 F
  • 42,307
  • 41
  • 144
  • 212
  • 1
    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 Oct 12 '10 at 07:34
  • 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 F Oct 12 '10 at 08:03
  • 1
    @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 Oct 12 '10 at 08:17
  • As a convenience to library users. This isn't so strange, libev does this, for instance. – Jack Kelly Oct 12 '10 at 12:08
  • "why do you need C++ wrapper": to take advantage of C++ features in providing a cleaner interface to C++ library uses, rather then asking them to ignore the features they're using in their project and do things "the C way". – cheshirekow Jul 20 '12 at 18:49
4

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
  • 219,715
  • 46
  • 258
  • 445
  • 1
    Right, but the *API* would be C anyhow, regardless of the implementation language. – Eamon Nerbonne Oct 12 '10 at 07:31
  • 1
    @Earmon: I'm not sure whether your comment disagrees with anything I wrote and, if so, with what. – sbi Oct 12 '10 at 07:32
  • So if you are able to write the implementation in C you have only one wrapper instead of two. – frast Oct 12 '10 at 07:35
  • @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 Oct 12 '10 at 08:27
  • 1
    @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 Oct 12 '10 at 09:57
  • @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 Oct 12 '10 at 10:06
  • @sbi: I agree. If the C++ API already exists or you have to use C++ libraries then a C wrapper is more reasonable. – frast Oct 12 '10 at 11:05
  • Out of curiosity, why did you wrap C++ around C around C++ instead of just calling the originals directly? – Eamon Nerbonne Oct 12 '10 at 11:11
  • @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 Oct 12 '10 at 12:33
  • @sbi: An amusing situation to find yourself in :D – Matt Joiner Oct 13 '10 at 13:44
3

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
  • 164,997
  • 36
  • 182
  • 240
  • 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 Oct 12 '10 at 07:36
  • @Eamon: your assumption is flawed - wrapper functions are a case where it's normally acceptable to include function bodies in header files – Christoph Oct 12 '10 at 08:06
  • 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 Oct 12 '10 at 08:23
  • @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 Oct 12 '10 at 08:53
  • 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 Oct 12 '10 at 11:08
  • @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 Oct 12 '10 at 12:11
  • Yeah, true enough - the code size issue is potentially quite relevant. – Eamon Nerbonne Oct 12 '10 at 12:14
  • Huh? Why isn't the linker optimizing functions from other object files? I thort this was common practise? – Matt Joiner Oct 13 '10 at 13:48
  • 1
    @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 Oct 13 '10 at 22:48
  • @Christoph: Thanks! I had no idea. – Matt Joiner Oct 13 '10 at 23:11
2

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,749
  • 17
  • 23
2

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
  • 2,700
  • 1
  • 25
  • 34
2

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.

Prof. Falken
  • 24,226
  • 19
  • 100
  • 173
2

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 Developing C wrapper API for Object-Oriented C++ code

Community
  • 1
  • 1
Michael Anderson
  • 70,661
  • 7
  • 134
  • 187