Previous section   Next section

Imperfect C++ Practical Solutions for Real-Life Programming
By Matthew Wilson
Table of Contents
Chapter 7.  ABI


7.1. Sharing Code

Suppose that you've implemented a library of some useful classes, and want to make it available for other programmers to use. Furthermore, let's say that some of these other programmers are using different operating systems and/or different compilers than the one used to develop the software. How do you do it?

Well, the most obvious solution, although not necessarily the most desirable, is to simply let them have all the source code, and build it themselves. In theory, that should work fine, since all the compilers should be able to deal with your C++ code, right? Unfortunately, as you have probably already experienced, or at least gleaned from this book, the complexity of C++ is such that compilers have their own dialectical differences, and the chances are that your source code will require some nontrivial persuasion to work with other compilers: this is especially so when dealing with very modern techniques, such as template meta-programming (TMP). If your code is to be used on another operating system, it is overwhelmingly likely that you'll need to port it—modify various calls, algorithms, even whole subsystems—to the new platform, so the issue of binary compatibility becomes moot. Let's put these matters aside, however, for the moment, and stipulate that sharing at the source-code level is, in principle, a relatively straightforward concept, even if in reality it is sometimes less so.[1]

[1] Reality can be harsh: one of my friend's development teams spent four months porting code that worked correctly on three variants of UNIX to a fourth. Ouch!

The advantages of supplying binary-only are not solely the protection of intellectual property, but also that you retain control of any fixes or updates to the code. The downside is that in order to avail themselves of improvements users need to obtain new copies of the libraries, and relink their projects. Conversely, when you supply source, there is nothing stopping your clients effecting their own fixes or enhancements (or sometimes the opposite). However, unless they take care to apprise you of the fixes and you take care to incorporate them into the future versions, the next time they upgrade your software their fixes will be overwritten and need to be reapplied.

For whatever reason—paranoia, protection of intellectual property, convenience, shame—you don't want to expose the internals of your library to the outside world, and wish to provide it in binary form. In an ideal world, you'd compile your implementation files into a binary library and supply it, along with the appropriate header files, to your clients. They would #include the headers in their client code and link to your binary library. What could be simpler?

Alas, this rosy picture does not reflect reality. For it to work there would need to be an agreed representation for the compiled forms and mechanisms of the types, functions, and classes that are described in your headers. Such a thing is called an Application Binary Interface, or ABI, and C++ does not have one!

Imperfection: The C++ standard does not define an ABI, leading to wholesale incompatibilities between many compilers on many platforms.


In recent times there have been moves to standardize ABIs for C++, notably the Itanium C++ ABI [Itan-ABI]. But in general the above imperfection holds.

Since C++ is a compiled-to-machine-code language, there is no requirement to run binaries built for one platform on a different one, so it's more accurate to speak of C++ lacking ABI for specific platforms, that is, combinations of architecture and operating system. In this chapter we're going to look at the issues encountered on the Win32-Intel x86 platform. In some senses Microsoft represents a de facto standard for Windows development, since compilers must, at a minimum, be able to interact with the C functions provided in the Win32 system libraries. But this hardly represents a true ABI, and there are a great many incompatibilities between compiler vendors on this one platform.

For a language as complex as C++, there are many aspects that would have to be standardized to support an ABI. These include object layout (including base classes), calling conventions and name mangling, the virtual function mechanism, static object instantiation, C++ language support (e.g., set_unexpected(), operator new[](), and so on), exception handling and run time type information (RTTI; C+-98: 18.5), and template instantiation mechanisms. There are also less obvious issues, such as run time library memory debugging facilities, which are not part of the language per se, but which nevertheless have significant practical impact.

Left without direction or control, different compiler vendors have inevitably come up with different implementations of C++ (and C) run time formats, behavior, and infrastructure; whether the result of a natural diversion, or deliberate commercial tactics, is outside the scope of this book. But whatever the reason is we can say that this is a big problem. A good friend of mine[2] summarizes it thus:

[2] Sadly, he's not much of a friend of C++ anymore, for this and several other important imperfections. Splitter!

The biggest fault in C++, for my money, is the lack of a standard for the ABI. As a direct consequence C++ code compiled with GCC won't work with C++ code compiled with MSVC unless you provide a 'C' interface (using extern "C" linkage). This forces all C++ providers looking to provide generic, reusable code to use 'C' linkage at some point.

He's right, up to a point. If I provide a binary library built with one compiler, there is a good chance that it will not be compatible with code compiled with another compiler. In other words, I cannot deliver a single binary version of my library; rather I may need to provide versions for all possible compilers used by potential clients.

Naturally, the commercial sway of some compiler vendors has caused there to be some coalescence of compatibility. So it is possible for me to build C++ libraries in, say, CodeWarrior and Intel, and link the binary form into an executable built with Visual C++. But a partial solution is no solution unless you're prepared to restrict yourself to a single compiler or a small set of compilers.

Until the movements toward unified per-platform ABIs such as the Itanium project become the norm, we are faced with the thorny problem that my erudite friend identifies. This chapter and the next acknowledge the problem, but do not acknowledge the complete defeat that he implies. Let's look at the measures we can take to ease our discomfort.


      Previous section   Next section