DOC HOME SITE MAP MAN PAGES GNU INFO SEARCH PRINT BOOK
 
Instantiating C++ templates

More on linking template code into archives

There are some unique problems that can be encountered when using automatic instantiation and preparing archive libraries (.a files) that contain template-based code. However, the bottom line is that the automatic instantiation system should handle most of them without user invention; the material in this section is for rare situations where this does not happen, or for those looking to fine-tune their instantiations.

To begin with, consider this simple example:

   $ cat A.h
   template<class T>
   class A {
   public:
       int fff();
       int ggg();
       int hhh();
   };
   

#include "A.C"

$ cat A.C template<class T> int A<T>::fff() { return (T) 1; }

template<class T> int A<T>::ggg() { return (T) 2; }

template<class T> int A<T>::hhh() { return (T) 3; }

$ cat al1.C #include "A.h"

int al1() { A<int> a; return a.fff() + a.ggg(); }

$ cat al2.C #include "A.h"

int al2() { A<int> a; return a.fff() + a.hhh(); }

$ cat main.C int al1(); int al2();

int main() { return al1() + al2(); }

The main function calls two other functions. The two other functions each call member functions of the same template instantiation.

However, as it happens the two other functions are in separate archive libraries, so we build the first library:

   $ CC -c al1.C
   $ CC -Tprelink_objects al1.o
   prelink: INFO: C++ prelinker: executing: CC -c al1.C
   $ ar rv libal1.a al1.o
   a - al1.o
   UX:ar: INFO: Creating libal1.a
and then the second library:
   $ CC -c al2.C
   $ CC -Tprelink_objects al2.o
   prelink: INFO: C++ prelinker: executing: CC -c al2.C
   $ ar rv libal2.a al2.o
   a - al2.o
   UX:ar: INFO: Creating libal2.a
Now, pretend we are using an earlier release of the UDK C++ compiler. We build the main program executable and link it against the two archive libraries.
   $ CC -c main.C
   $ CC -L. main.o -lal1 -lal2
   UX:ld: ERROR: ./libal2.a(al2.o): fatal error: symbol `A<T1>::fff(void) [with T1=int, return type=int]` multiply-defined, also in file ./libal1.a(al1.o)
What happened? After the prelinking and archiving, libal1.a:al1.o has definitions of "A<int>:fff()" and "A<int>:ggg()", while libal2.a:al2.o has definitions of "A<int>:fff()" and "A<int>:hhh()". When the main program pulls in libal1.a:al1.o to resolve al1() and libal2.a:al2.o to resolve al2(), it also pulls in both definitions of "A<int>:fff()", and hence the error.

This is a very common situation to get into when using archive libraries for template-based code. There are four possible solutions:

  1. Do no instantiations in the archives (which means in practice omitting the prelinking step), but instead do all of the instantiations when linking the executable. This gets around the multiple definition error, but presents equally difficult other problems.

  2. Another way would be to abandon automatic instantiation and manually assign instantiations to individual .o's, knowing ahead of time how the .o's will be combined into archives and used by executables. This will work for small cases, but quickly becomes hopelessly infeasible for real-sized projects.

  3. The straightforward solution adopted in current releases of the UDK C++ compiler, is to generate all template instantiations (of both template functions and template static data members) as "weak symbols". See ``Handling multiply defined symbols''. This means that the multiple definitions can co-exist together without producing a linker error; all references to a symbol are bound to one copy of the symbol, and the other copies are ignored. This approach has the great advantage of being transparent to the user, and in the great majority of cases is the way to go.

    However, there are two disadvantages. Extra copies of template instantiations are still kept in the .o files making up the archive, and depending upon the application this could lead to significant code bloat.


    NOTE: This code bloat only exists when archives are involved; the normal automatic instantiation scheme prevents this from happening in other kinds of links.

    In addition, certain arrangements of template source code and usages, especially those developed under the gcc compiler using the -frepo option, may not be linkable in this approach without significantly rearranging the contents of the archives, a rearrangement that may not be feasible.

  4. The fourth way is presented in the next section.

Next topic: The one instantiation per object scheme
Previous topic: Libraries

© 2005 The SCO Group, Inc. All rights reserved.
SCO OpenServer Release 6.0.0 -- 02 June 2005