Binary interfaces in component development
A template class with static data - how can that possibly go wrong?
As we know, with few exceptions, a C++ compiler instantiates a template whenever it is specialised, and the template definitions are available (see the ISO C++ standard paragraph 14.7.1). In our code, the template definitions are in the header file so the template definitions are always available. This means that each cpp file that uses the template will contain instantiated definitions of the static object in the compiled object file. When object files are linked, the linker notices that there are many definitions of a single symbol and it merges all definitions into a single definition. Such symbols are marked in the object file as “weak”, so that the linker will unify multiple definitions instead of complaining about a multiply-defined symbol. A counter example of a “strong” symbol is a non-inline function definition; here the linker would give an error if it came across a duplicate definition.
When these definitions all occur in a single monolithic binary, there are no problems. This is shown by the following example, where we introduce a singleton class that we implement via our
GenericSingleton template. If the implementation works correctly then this object will be constructed and destructed only once. To test our singleton we add two further cpp files containing code that uses the resulting singleton, and a further cpp containing the main function. In our initial test, we run it as a monolithic binary and we expect that only a single singleton object will be constructed – see Figure 1.
The above is the case where our binary is linked monolithically, using only static libraries and everything works. Now, imagine a scenario where your manager wants to decompose the project, so that different project teams can be responsible for their own dynamic library, so that development can occur in parallel, so that functionalities can be unit tested without rebuilding everything - and for all the other reasons that motivate people to use dynamic libraries. This manager, like lots of people, comes from a planet where an application can be built interchangeably, either statically or dynamically, with no difference in behaviour.
I’m sure we’d all like to come from this programmer’s paradise too, but until we can find a way to move there, maybe we’ll have to support our user base here on Earth with its less than perfect operating systems. So, what will happen when we take our simple program and change it from a monolithic binary into a set of dynamically-linked components?