Contents|Index|Previous|Next
Wheres
the Template?
C++ templates are the first
language feature to require more intelligence from the environment than
one usually finds on a UNIX system. Somehow the compiler and linker have
to make sure that each template instance occurs exactly once in the executable
if it is needed, and not at all otherwise.
There are two basic approaches
to this problem, which we will refer to as the Borland model and
the Cfront model.
Borland model
Borland C++ solved the template instantiation problem by adding the code
equivalent of common blocks to their linker; template instances are emitted
in each translation unit that uses them, and they are collapsed together
at run time. The advantage of this model is that the linker only has to
consider the object files themselves; there is no external complexity to
worry about. This disadvantage is that compilation time is increased because
the template code is being compiled repeatedly. Code written for this model
tends to include definitions of all member templates in the header file,
since they must be seen to be compiled.
Cfront model
The AT&T C++ translator, Cfront, solved the template instantiation
problem by creating the notion of a template repository, an automatically
maintained place where template instances are stored. As individual object
files are built, notes are placed in the repository to record where templates
and potential type arguments were seen so that the subsequent instantiation
step knows where to find them. At link time, any needed instances are generated
and linked in. The advantages of this model are more optimal compilation
speed and the ability to use the system linker; to implement the Borland
model a compiler vendor also needs to replace the linker. The disadvantages
are vastly increased complexity, and thus potential for error; theoretically,
this should be just as transparent, but in practice it has been very difficult
to build multiple programs in one directory and one program in multiple
directories using Cfront. Code written for this model tends to separate
definitions of non-inline member templates into a separate file, which
is magically found by the link preprocessor when a template needs to be
instantiated.
Currently, G++ implements
neither automatic model. In the mean time, you have several options for
dealing with template instantiations.
- Compile your template-using
code with -frepo.
The compiler will generate files with the extension .rpo
listing all of the template instantiations used in the corresponding object
files which could be instantiated there; the link wrapper, collect2,
will then update the .rpo
files to tell the compiler where to place those instantiations and rebuild
any affected object files. The link-time overhead is negligible after the
first pass, as the compiler will continue to place the instantiations in
the same files.
- This is your best option
for application code written for the Borland model, as it will just work.
Code written for the Cfront model will need to be modified so that the
template definitions are available at one or more points of instantiation;
usually this is as simple as adding #include
<tmethods.cc>
to the end of each template header.
- For library code, if you
want the library to provide all of the template instantiations it needs,
just try to link all of its object files together; the link will fail,
but cause the instantiations to be generated as a side effect. Be warned,
however, that this may cause conflicts if multiple libraries try to provide
the same instantiations. For greater control, use explicit instantiation
as described in the next option.
- Compile your code with -fno-implicit-templates
to disable the implicit generation of template instances, and explicitly
instantiate all the ones you use. This approach requires more knowledge
of exactly which instances you need than do the others, but its less mysterious
and allows greater control. You can scatter the explicit instantiations
throughout your program, perhaps putting them in the translation units
where the instances are used or the translation units that define the templates
themselves; you can put all of the explicit instantiations you need into
one big file; or you can create small files for each of the instances you
need, like the following examples define, and
create a template instantiation library from those files.
#include "Foo.h"
#include "Foo.cc"
template class Foo<int>;
template ostream& operator <<
(ostream&, const Foo<int>&);
If you are using Cfront-model
code, you can probably get away with not using -fno-implicit-templates
when compiling files that dont #include
the member template definitions. If you use one big file to do the instantiations,
you may want to compile it without -fno-implicit-templates
so you get all of the instances required by your explicit instantiations
(but not by any other files) without having to specify them as well.
G++ has extended the template
instantiation syntax outlined in the Working Paper to allow forward declaration
of explicit instantiations, explicit instantiation of members of template
classes and instantiation of the compiler support data for a template class
(i.e., the vtable) without instantiating any of its members as the following
example shows.
extern template int max (int, int);
template void Foo<int>::f ();
inline template class Foo<int>;
Do nothing. Pretend G++
does implement automatic instantiation management. Code written for the
Borland model will work fine, but each translation unit will contain instances
of each of the templates it uses. In a large program, this can lead to
an unacceptable amount of code duplication.
Add #pragma
interface to
all files containing template definitions. For each of these files, add
#pragma implementation
"filename"
to the top of some .C
file which #includes
it. Then compile everything with-fexternal-templates.
The templates will then only be expanded in the translation unit which
implements them (i.e., the translation unit has a #pragma
implementation
line for the file where they live); all other files will use external references.
If youre lucky, everything should work properly. If you get undefined
symbol errors, you need to make sure that each template instance which
is used in the program is used in the file which implements that template.
If you dont have any use for a particular instance in that file, you can
just instantiate it explicitly, using the syntax from the C++ working paper.
template class A<int>;
template ostream& operator << (ostream&, const A<int>&);
This strategy will work
with code written for either model. If you are using code written for the
Cfront model, the file containing a class template and the file containing
its member templates should be implemented in the same translation unit.
A slight variation on this
approach is to use the flag -falt-external-templates
instead; this flag causes template instances to be emitted in the translation
unit that implements the header where they are first instantiated, rather
than the one which implements the file where the templates are defined.
This header must be the same in all translation units, or things are likely
to break.
See Declarations
and Definitions in One Header for more discussion of these pragmas.