Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C

Please Have a C API for Your Library

5.00/5 (5 votes)
27 Jan 2018CPOL3 min read 8.4K  
Please have a C API for your library

C++ can be terrible when crossing library and build system boundaries. To provide yet another case in point, let me document for posterity a problem I recently ran into when bundling WebRTC as a third party library in another project - I hope it will save time for someone in the future. So, having sorted out linker problems due to compiling against different C++ standard libraries (libstdc++ vs libc++ - which perhaps deserves discussion on its own), duplicated symbols between BoringSSL and OpenSSL (Ok, this was not a C++ problem) and similar nuisances, I finally had just a couple of linker errors, albeit somewhat cryptic:

C
undefined reference to `non-virtual thunk to buzz::IqTask::HandleStanza(buzz::XmlElement const*)'

To make it a bit less cryptic: in case of classes with multiple inheritance, vtbl (table of virtual functions) may not contain pointers to the virtual functions directly, because depending on which pointer the method is called from, an offset may need to be added or subtracted from this pointer before calling that method. This is why virtual table in subclasses points to those short "thunk" functions that usually do just add/sub and jmp - see here and ultimately here for details0.

So, WebRTC's libjingle has a header file which defines class IqTask (a descendant of XmppTask and a couple other classes, therefore multiple inheritance is involved), and that class has a virtual method HandleStanza(). Both our code and WebRTC code include that header, so both need to define a virtual table for that class. Apparently, I missed something and compiler stripped out that thunk from the library or did not generate it altogether?

However, quick check with nm and c++filt did not reveal anything wrong - at first glance, the thunk, referenced in our object file, is present in the library and should link right in:

C
nm -po *.o | c++filt | grep IqTask::HandleStanza
C
Module.XMPP.cpp.o:             U non-virtual thunk to buzz::IqTask::HandleStanza(buzz::XmlElement const*)
C
librtc_xmpp.a:rtc_xmpp.iqtask.o:0000000000000360 
T non-virtual thunk to buzz::IqTask::HandleStanza(buzz::XmlElement const*)

Baffled, I spent some time reading up on how to better debug the linker, before an idea struck me - c++filt may be masking the problem, I need to see the symbols as they are! And indeed - thunk's mangled names were different between the object files (produced by different build processes, mind you):

C
Module.XMPP.cpp.o:                 U _ZThn136_N4buzz6IqTask12HandleStanzaEPKNS_10XmlElementE
C
librtc_xmpp.a:rtc_xmpp.iqtask.o:0000000000000360: 
T _ZThn160_N4buzz6IqTask12HandleStanzaEPKNS_10XmlElementE

I checked the source of __cxa_demangle() for the meaning of those numbers, but did not find any clue how to narrow down the cause of the offset discrepancy. Figuring out that this might have been due to mismatch of class layout or number of methods, which should boil down to differences in compiler options or defined symbols, I proceeded to comparing those.

I'll spare you the process of adding/removing compiler flags in both the build systems (for our code and for the library, which uses gyp), and fast-forward to the end: the difference was caused by _GLIBCXX_DEBUG defined by WebRTC in Debug configuration "just in case". Having unset that (by hacking the .gyp file), I was able to produce the library files with the compatible class layout.

This is not the only problem experienced with the library due to it exposing a C++ API, but that's enough to make a point. C++ is a great language for standalone applications, but for anything that needs binary stability, be it run-time or compile-time (i.e., libraries, shared objects), it is a wrong choice. Some of the problems apply to C as well, but linking to a C library is usually much easier, as well as taking control of its memory allocation (which is another feature that WebRTC regrettably lacks).

I'm beating a dead horse here, and the point of C++ being unsuitable for interfacing with other software has been long made. Still, there are libraries with C++ interfaces out there, that, at least in non-trivial projects, cause more pain than gain. Hence, I kindly ask all current and future library authors: no matter what language you use internally, please, provide a C API to your library - like, literally, extern "C" {...}. Thank you in advance.

0. If you decide to compare this with nullptr, more trickery will be needed on compiler side because the pointer may no longer be arithmetically 0 due to these adjustments.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)