Separate Compilation
Separate Compilation These guidelines are designed for authors of Boost libraries which have separate sources that need compiling in order to use the library. Throughout, this guide refers to a fictitious "whatever" library, so replace all occurrences of "whatever" (or "WHATEVER") with your own library’s name when copying the examples.
Changes Affecting Source Code Preventing Compiler ABI Clashes There are some compilers (mostly Microsoft Windows compilers), which feature a range of compiler switches that alter the (Application Binary Interface) ABI of C++ classes and functions. By way of example, consider Borland’s compiler which has the following options:
Option Description -b
on or off - effects enum sizes
-Vx
on or off - empty members
-Ve
on or off - empty base classes
-aX
alignment - 5 options
-pX
Calling convention - 4 options
-VmX
member pointer size and layout - 5 options
-VC
on or off, changes name mangling
-Vl
on or off, changes struct layout
These options are provided in addition to those affecting which runtime library is used (more later); the total number of combinations of options can be obtained by multiplying together the individual options above, so that gives 2x2x2x5x4x5x2x2 = 3200 combinations!
The problem is that users often expect to be able to build the Boost libraries and then just link to them and have everything just plain work, no matter what their project settings are. Irrespective of whether this is a reasonable expectation or not, without some means of managing this issue, the user may well find that their program will experience strange and hard to track down crashes at runtime unless the library they link to was built with the same options as their project (changes to the default alignment setting are a prime culprit). One way to manage this is with "prefix and suffix" headers: these headers invoke compiler specific #pragma directives to instruct the compiler that whatever code follows was built (or is to be built) with a specific set of compiler ABI settings.
Boost.Config provides the macro BOOST_HAS_ABI_HEADERS which is set whenever there are prefix and suffix headers available for the compiler in use, typical usage in a header like this:
ifndef BOOST_WHATEVER_HPP #define BOOST_WHATEVER_HPP
include
// this must occur after all of the includes and before any code appears: #ifdef BOOST_HAS_ABI_HEADERS
include BOOST_ABI_PREFIX
endif // // this header declares one class, and one function by way of examples: // class whatever { // details. };
whatever get_whatever();
// the suffix header occurs after all of our code: #ifdef BOOST_HAS_ABI_HEADERS
include BOOST_ABI_SUFFIX
endif
endif Copied! You can include this code in your library source files as well if you want, although you probably shouldn’t need to:
If you don’t use these in the library source files (but do in your library’s headers) and the user attempts to compile the library source with a non-default ABI setting, then they will get compiler errors if there are any conflicts.
If you do include them in both the library’s headers and the library source files, then the code should always compile no matter what the compiler settings used, although the result might not match what the user was expecting: since we’ve forced the ABI back into default mode.
Rationale Without some means of managing this issue, users often report bugs along the line of "Your silly library always crashes when I try and call it" and so on. These issues can be extremely difficult and time consuming to track down, only to discover in the end that it’s a compiler setting that’s changed the ABI of the class and/or function types of the program compared to those in the pre-compiled library. The use of prefix/suffix headers can minimize this problem, although probably not remove it completely.
Counter Argument #1 Trust the user, if they want 13-byte alignment (!) let them have it.
Counter Argument #2 Prefix/suffix headers have a tendency to "spread" to other boost libraries - for example if boost::shared_ptr<> forms part of your class’s ABI, then including prefix/suffix headers in your code will be of no use unless shared_ptr.hpp also uses them. Authors of header-only boost libraries may not be so keen on this solution - with some justification - since they don’t face the same problem.
Static or Dynamic Libraries When the users runtime is dynamically linked the Boost libraries can be built either as dynamic libraries (.so on Unix platforms, .dll on Windows, .dylib on macOS) or as static libraries (.a on Unix or macOS, .lib on Windows). So we have a choice as to which is supported by default:
On Unix platforms it typically makes no difference to the code: the user just selects in their makesfile which library they prefer to link to.
On Windows platforms, the code has to be specially annotated to support DLL’s, so we need to pick one option as the default and one as an alternative.
On Windows platforms, we can inject special code to automatically select which library variant to link against: so again we need to decide which is to be the default (see the section on Auto-linking below).
The recommendation is to pick static linking by default.
Rationale There is no one policy that fits all here.
The rationale for the current behaviour was inherited from Boost.Regex (and it’s ancestor regex++): this library originally used dynamic linking by default whenever the runtime was dynamic. It’s actually safer that way should you be using regex from a dll for example. However, this behavior brought a persistent stream of user complaints: mainly about deployment, all asking if static linking could be the default. After regex changed behavior the complaints stopped, and the author hasn’t had one complaint about static linking by default being the wrong decision.
Note that other libraries might need to make other choices: for example libraries that are intended to be used to implement dll pluggin’s would like need to use dynamic linking in almost all cases.
Supporting Windows Dll’s On most Unix-like platforms no special annotations of source code are required in order for that source to be compiled as a shared library because all external symbols are exposed. However the majority of Windows compilers require that symbols that are to be imported or exported from a dll, be prefixed with declspec(dllimport) or declspec(dllexport). Without this mangling of source code, it is not possible to correctly build shared libraries on Windows (historical note - originally these declaration modifiers were required on 16-bit Windows where the memory layout for exported classes was different from that of "local" classes - although this is no longer an issue, there is still no way to instruct the linker to "export everything", it also remains to be seen whether 64-bit Windows will resurrect the segmented architecture that led to this problem in the first place. Note also that the mangled names of exported symbols are different from non-exported ones, so __declspec(dllimport) is required in order to link to code within a dll).
In order to support the building of shared libraries on MS Windows your code will have to prefix all the symbols that your library exports with a macro (lets call it BOOST_WHATEVER_DECL) that your library will define to expand to either declspec(dllexport) or declspec(dllimport) or nothing, depending upon how your library is being built or used. Typical usage would look like this: