f78
Where's the Template?
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
ffb
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 out
f14
lined 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.
0