63

I recently was wondering when to use C over C++, and vice versa? Fortunately someone already beat me to it and although it took a while, I was able to digest all the answers and comments to that question.

However one item in that post keeps being addressed again and again, without any kind of example, verification or explanation:

"C code is good for when you want to have multiple language bindings for your library"

That's a paraphrase. I should note that several people point out that multiple language bindings are possible in C++ (via some extern functioning), but nevertheless, if you read that post in its entirety, it's pretty obvious that C is ideal for portability/language binding. My question is: why?

Can someone please provide concrete reasons why writing libraries in C allows for easier bindings and/or portability in other languages?

8
  • 6
    Mostly for historical and social reasons. Most language implementations and runtime systems are built above C. For instance, Ocaml, SBCL, Haskell runtimes are coded in C (and won't gain much by being recoded in C++) Commented May 4, 2015 at 10:13
  • 8
    In practice, gluing genuine C++ libraries (think of Qt) into these runtimes is painful. Commented May 4, 2015 at 10:15
  • 3
    @Basile: Qt isn't a genuine C++ library. Also, even for more painful libraries, it's only painful because they express actually useful semantics.
    – DeadMG
    Commented May 4, 2015 at 10:16
  • 2
    Semi-related: programmers.stackexchange.com/a/185159/20756
    – Blrfl
    Commented May 4, 2015 at 11:03
  • 2
    Read Essential COM by Don Box. It explains the process of creating a ABI in C++. COM was Microsoft's way of creating an ABI. i.e. COM C++ libraries could be used from C or VB or other languages.
    – user93353
    Commented May 5, 2015 at 14:33

8 Answers 8

72

C has a much, much simpler interface, and its rules for converting a source code interface into a binary interface are straightforward enough that generating external interfaces to bind to is done in a well-established manner. C++, on the other hand, has an incredibly complicated interface, and the rules for ABI binding are not standardized at all, neither formally nor in practice. This means that pretty much any compiler for any language for any platform can bind against an external C interface and know exactly what to expect, but for a C++ interface, it's essentially impossible because the rules change depending on which compiler, which version, and which platform the C++ code was built with.

In C, there's no standard binary language implementation rules, either, but it's an order of magnitude simpler and in practice compilers use the same rules. Another reason making C++ code hard to debug is the above-mentioned complicated grammar, since debuggers frequently can't deal with many language features (place breakpoints in templates, parse pointer casting commands in data display windows, etc.).

The lack of a standard ABI (application binary interface) has another consequence - it makes shipping C++ interfaces to other teams / customers impractical since the user code won't work unless it's compiled with the same tools and build options. We've already seen another source of this problem - the instability of binary interfaces due to the lack of compile time encapsulation.

-- Defective C++

11
  • 4
    You should note that, while it is possible to define a C-like API implemented in C++ (extern "C", it is still more additional work to do so, that should be balanced against the features provided by C++)
    – jhominal
    Commented May 4, 2015 at 10:31
  • 5
    The C++ ABI is irrelevant in the context of the question, where C++ libraries can be easily exported with extern "C".
    – DeadMG
    Commented May 4, 2015 at 10:33
  • 13
    @jhominal: It's a lot better than writing it in C, where you have to define a C interface and then you also have to implement it in C, whereas in C++ you can define a C interface and then you don't have to implement it in C. No matter what language you're implementing in, you still have to define a C interface- this is of course true in C++ as it is in C or any other language that can expose C bindings.
    – DeadMG
    Commented May 4, 2015 at 10:35
  • 9
    Would it be possible to add a disclaimer to the link to that FQA drivel?
    – Martin Ba
    Commented May 4, 2015 at 10:46
  • 2
    @DocBrown: Sure looks to me like the question is about C language bindings vs. C++ language bindings. Commented May 4, 2015 at 13:41
33

If you're trying to communicate with a speaker of another language, pidgin is easier than Shakespearean English.

C's concepts - function calls, pointers, NULL-terminated strings - are very straightforward, so other languages can easily implement them well enough to call C functions. For historical reasons, many other languages are implemented in C, which makes calling C functions even easier.

C++ adds quite a bit of stuff - classes, with inheritance and vtables and access modifiers; exceptions, with stack unwinding and changing the flow of control; templates. All of this makes it harder for other languages to use C++ bindings: at best, there's more "glue" or interoperability code to implement, and at worst, the concepts don't translate directly (due to differences in class models, exception handling, etc.). For templates in particular, simply using (instantiating) them typically requires a compilation step with a C++ compiler, which vastly complicates using them from other environments.

With all of that said, it's possible to overstate the difficulty of providing bindings from a C++ library to another language:

  • C++ bindings can be just as compatible as C, if you're willing to work at it. As @DeadMG points out, C++ supports extern "C", so you can export C-style language bindings (with all of the simplicity and compatibility of C bindings) from a C++ library (with the limitation that you can't expose any C++-specific functionality).
  • Another common objection to C++ language bindings is the lack of ABI stability for C++, but this too is overstated; C++ ABIs are less standardized than C ABIs, but standards and de facto standards do exist (the Itanium C++ ABI, which is also used on OS X; GCC's de facto standard for Linux). Windows is worse, but even on Windows, staying within one Visual C++ version should work fine.
3
  • 1
    The other issue with providing a binding from a C++ library to another language is that the other language may require bindings to be implemented in C. Or, for languages that have something like (.NET) P/Invoke or (python) ctypes, it may not provide any tools for using the C++ ABI.
    – Random832
    Commented May 4, 2015 at 13:34
  • 6
    @Random832: Which is completely irrelevant when the C++ side can simply offer the C interface. You don't have to implement the binding in C to offer a C binding.
    – DeadMG
    Commented May 4, 2015 at 14:33
  • 1
    People who have had to cope with a change of C++ ABI on the platform they were using tend to be adverse to doing it again. I've dealt with two (Sun C++ v4 to v5, and GCC 3,3 to 3,4) and will always recommend a C binding over a C++ one. Commented Nov 5 at 21:14
23

C is one of the oldest languages still around. Its ABI is simple, and virtually every operating system still in use today has been written in it. While some of those OS's may have added stuff e.g. in C#/.NET or whatever on-top, down below they're very much steeped in C.

That means that, in order to use the functionality provided by the OS, virtually every programming language out there needed a way to interface with C libraries anyway. Perl, Java, C++, they all natively provide ways to "talk C", because they had to if they didn't want to reinvent every single wheel there is.

This makes C the Latin of programming languages. (How many years of internet before that metaphor has to be "the English of progamming languages"?)


When you are writing your library in C, you get a C-compatible interface for free (obviously). If you are writing your library in C++, you can get C bindings, via extern "C" declarations as you mentioned.

However, you can get those bindings only for functionality that can be expressed in C.

So your library API cannot make use of...

  • templates,
  • classes,
  • exceptions,
  • any functions taking or returning objects.

One simple example, you would need to make your exported functions take and return arrays ([]) instead of std::vector (or std::string for that matter).

So, not only would you be unable to provide any of the good things C++ has to offer to your library's clients, you also would have to go to additional effort to "translate" your library API from C++ to "C compatible" (extern "C").

That is why the point could be made that C is the better choice for implementing a library. Personally, I think the benefits of C++ still outweigh the necessary effort for a extern "C" API, but that's just me.

7
  • Windows seems to be trying to base itself around .NET, Android is based around Java (also C as an implementation detail for some APIs), and iOS/OSX are based around Objective-C. Commented May 4, 2015 at 21:32
  • 1
    English is already the dominant language in the programming world. More dominant than in other professions.
    – Siyuan Ren
    Commented May 5, 2015 at 1:45
  • 3
    @immibis: Windows, Linux/Android, and BSD/OSX are all kernels written in C, with interfaces written in (and for) C. No matter what is built in top of that, Java needs JNI, Perl needs C calls, .NET needs C calls, Python needs C calls, Objective-C needs C calls. None of those need C++ calls, which is the point I was trying to make.
    – DevSolar
    Commented May 5, 2015 at 5:21
  • @DevSolar A lot of Android's stuff is written natively in Java and doesn't use JNI (you can use it "backwards" to call Java code from C, but that just further confirms that it's natively Java). No experience with iOS/OSX but I hear those are the same with Objective-C. Commented May 5, 2015 at 5:36
  • 3
    @immibis: But you do know the difference between a OS kernel and its userspace, don't you? I seriously doubt a mostly Java userspace makes Android any less a Linux kernel with C-style system calls than the Linux kernel running on my desktop. I also doubt they are using Java in the kernel, or in the middleware. Actually, I know they're using C there. It's the hen-and-egg problem, just the other way round. It's been done in C before, and thus it's so much easier to still do it in C.
    – DevSolar
    Commented May 5, 2015 at 6:38
6

Leaving out the details other answers already provide:

The reason so many languages provides a C binding is that all *nix and Windows operating systems expose most of their OS API via a C interface. So the language implementation already needs to interface with C to be able to run on the major Oses. Therefore, it is straightforward to also offer directly communicating with any C interface from the language itself.

5

There is no reason. If the semantics you're trying to express are fundamentally C-compatible and not something like templates, there is no reason you can bind easier if the implementation is written in C. In fact, it's pretty much by definition that a C interface can be filled out by any implementation that can meet the binary contract- including an implementation in another language. There are languages other than C++ that can implement C binary contracts that can work in this fashion.

What it really boils down to is people who don't want to learn new languages or ideas that have actually useful semantics or features desperately trying to pick any reason to stay in the dinosaur era.

7
  • 4
    I think you are correct and the downvoters misunderstood the question, but actually your answer misses to highlight that potential misunderstanding: the question is not about "a library with a C interface" vs. "a library with a C++ interface", it is about "a library written entirely in C" vs. "a library with a C interface written in C++".
    – Doc Brown
    Commented May 4, 2015 at 13:01
  • @Snowman: Having problematic C++ bindings has absolutely nothing to do with any problem with exposing C bindings.
    – DeadMG
    Commented May 4, 2015 at 13:29
  • 2
    So you're going to pick a language that has useful semantics and features, and then force yourself to translate those into a C interface? While classes and templates may be avoidable (but call into question why you're using C++ in the first place), every function with C binding has to catch exceptions instead of letting them escape into the C code. Not to mention that anyone using your C-compatible library now has to deal with C++ and its ABI clashes and library mismatches, in addition to the substrate C's ABI clashes and library mismatches.
    – prosfilaes
    Commented May 5, 2015 at 7:01
  • 2
    @prosfilaes: It's trivial to convert exceptions into return codes. You don't need to avoid using classes as those can be easily lowered into a C API and you don't need to avoid using templates in your implementation. And your users don't need to give a shit about C++ ABI clashes unless they're building from source, in which case, you have to deal with the source language no matter what that is.
    – DeadMG
    Commented May 5, 2015 at 8:07
  • 4
    I downvoted not because one should necessarily not write a library with a C API in C++, but because there are good reasons to use C besides being a dinosaur. If you're writing for an API in language X, be that C, FORTRAN, COBOL, BLISS, or Java there's always a time where it's going to be simpler, easier and more correct to write in that language then to write in a fancier, more fun language and interface the two.
    – prosfilaes
    Commented May 5, 2015 at 11:16
2

There are two major axes when interfacing with another language:

  • the concepts that the interface can carry over: just values? references? generics?
  • how the interface is implemented in "binaries" (called ABI)

C has an advantage over C++ on those two fronts:

  • C only has mostly simple concepts, which appear in most every other language1
  • The ABI of C binaries is decided by the OS2

Now, why do most languages have a similar set of concepts than C may be due to it either being "simple" or "pre-existing"; it does not matter though, the point is that they do.

On the contrary, C++ has complex concepts, and the ABI is decided by each compiler (though many adhere to the Itanimum ABI, except on Windows...). There was actually a proposal by Herb Sutter to have OSes fix a C++ ABI (on a per OS basis) to partially address this issue. Also, one should note that a C++ FFI is possible, D is attempting it3.

1 Except variadics (...), those are not simple

2 Does C have a standard ABI?

3 Interfacing D to Legacy C++ code

0

Fundamentally it comes down to the ABI standardisation. While neither C nor C++ has a standardised ABI that other languages can use to interface between binaries written, C has become a de-facto standard, everyone knows it and everyone else can use the same, simple, rules that the language has with respect to types and function calls.

C++ could have a standard ABI, but Stroustrup has said that he doesn't see the need for one. He also says it would be difficult to get consensus from compiler writers (though I doubt that - the C++ standard committee would issue a ABI similar to existing ones and compiler writers would simply alter the next version of their compilers, which are occasionally incompatible with binaries built with old versions of their compilers anyway - I recall recompiling a couple of libraries with a new Sun compiler and finding they failed to work with the old ones)

You'll note that that some companies have moved to using a standard ABI, Microsoft started this process with COM way back in the 90s, and today they've refined this into the WinRT ABI (not to be confused with the other WinRT that refers to a type of table OS) that allows programs written in C# to communicate with libraries written in C or C++ (ie Microsoft's own OS layer is written in C++, exposed using WinRT and consumed by C# applications when they call any OS runtime routine)

There's not a lot anyone can do unless a standards body steps up and fixes this situation. Microsoft obviously sees the value in it and has taken steps to resolve it for their platform.

So the answer is really C doesn't provide language bindings. It happens that nobody has listened and consumes them regardless.

-2

All answers fall short of the real problem: C++ compiling introduces "name mangling", so binaries are incompatible with "simple" function calls.

All ABI stuff is little more than an attempt to standardize it.

In general it's not guaranteed you can cross-link functions compiled with different compilers, even if you stick to plain C++. Formerly it was sure they were incompatible, but nowadays standardization is creeping in ;)

OTOH C was designed precisely to be a "high level Assembly" and allow all sort of easy interfacing. It should not surprise it's better suited to cross-language liking.

Side note: original C++ compiler (cfront) actually produced C source that had to be compiled, exactly like gcc that produces Assembly "under the hood".

Not the answer you're looking for? Browse other questions tagged or ask your own question.