DIGITAL C++
Using DIGITAL C++ for DIGITAL UNIX Systems


Previous Contents Index


Chapter 5
Using Templates

The DIGITAL C++ template instantiation model has been completely redesigned for Version 6.0. The changes include:

5.1 Overview

The C++ language includes the concept of templates. A template is a description of a class or function that is a model for a family of related classes or functions. Because templates are descriptions of classes or functions that are parameterizable according to the types they operate upon, they are sometimes called parameterized types.

For example, one can write a template for a Stack class, and then use a stack of integers, a stack of floats, and a stack of some user-defined type. In the source, these might be written Stack<int>, Stack<float>, and Stack<X>. From a single source description of the template for a stack, the compiler can create instantiations of the template for each of the types required.

An instantiation of a template creates a new type or function that applies the template's arguments to the template body. In the previous example, Stack<int> is a new type created by instantiation. In general, a template is instantiated when it is referenced. When a template class is instantiated, only those member functions that are referenced are materialized. Virtual functions are considered referenced when a constructor for the class is referenced. As part of the instantiation process, the compiler must materialize the bodies of the template functions that are used by the program.

The materialization of the template functions can be done automatically by DIGITAL C++, or can be done manually, using command-line options, pragmas, or explicit instantiation requests in the source code. In general, the automatic instantiation process satisfactorily handles the materialization of template functions. In some cases, the user might want to exercise finer control over the generation and placement of template functions. Usually, template instantiation requires that the template functions be instantiated at most once per program.

DIGITAL C++ supports a variety of template instantiation methods. The default mode of template instantiation is automatic instantiation. In this mode, DIGITAL C++ materializes the needed templates by default whenever they are referenced. The instantiated templates are placed in a repository, so that only one copy is present when the program is linked. DIGITAL C++ also supports three modes of manual instantiation from the command line. These are -define_templates, -tused, and -tlocal. Additionally, there are command-line switches that control where DIGITAL C++ looks for the definitions of templates.

5.2 Automatic Template Instantiation

When in automatic template instantiation mode, DIGITAL C++ attempts to instantiate every referenced template at compile time. After processing the source file, DIGITAL C++ runs through the list of templates that require instantiation and attempts to instantiate them by looking for the template definitions. If implicit inclusion is enabled, DIGITAL C++ attempts to find the source file that contains the template definition if the definition has not yet been seen. By default, implicit inclusion is enabled during normal compilation, and it is disabled when compiling with -E or -P. For automatic instantiation to work, at least one compilation that references a template function must be able to find the template definition. There is no restriction on where a template can be declared or defined, as long as the definition is visible to the compilation unit.

Instantiation object files are written to a directory called the repository. The names of the files written into the repository are based on the names of the entities being instantiated. By default the repository used is ./cxx_repository. The -ptr command-line option can be used to specify alternate directories to use as a repository. There is one object file in the repository for each instantiated template function, for each instantiated static data member, and for each virtual table required for virtual functions.

When the program is linked, the linker searches the repositories for needed template instantiations.

Instantiation object files in the repository are updated whenever a source file that requires the instantiation is compiled. Compiling with -M lists implicitly included files, so that the make utility can be used to build the programs and correctly recompile instantiations when needed. The instantiation object file is generated with the same command-line options as the compilation of the source file that generated it.

In some cases, DIGITAL C++ is unable to write template instantiations into the repository. When a template instantiation references a variable or function that is local to the source file, that variable or function is generated as part of the user's object file instead of in the repository. It is an error to link an application that has differing instantiations of the same template, which would be the case if a template that referenced local variables were instantiated in two or more different source files. An attempt to do so is diagnosed at link time with a multiply defined error.

DIGITAL C++ also provides an additional automatic template instantiation mode, to help improve the performance of automatic template instantiation.

The -ttimestamp automatic template instantiation mode is intended to reduce the time it takes to build an application that uses a large number of automatically instantiated templates in many source files. By default, automatic template instantiation always instantiates templates needed by the compilation into the repository, even if the instantiation already exists in the repository. This behavior guarantees that instantiations that are needed are up to date in the repository. However, this means that there is some compile time overhead regenerating instantiations that may already be up to date in the repository.

The -ttimestamp option changes the default automatic template instantiation behavior from always instantiating templates that are needed by the compilation, to instantiate if needed and the existing instantiation in the repository is not newer than the timestamp in the repository. That is, instantiations are added to the repository if they do not already exist, or are regenerated if the instantiation that is in the repository is older than the timestamp stored in the repository. The timestamp file is named TIMESTAMP and is in the repository.

This is immediately useful when building a system from scratch, starting with an empty repository. It saves the overhead of reinstantiation of unchanged code. In this case it is totally safe, because all instantiations that are needed are generated and up to date. Incremental application building would generally be done without this option, so that instantiations would overwrite the initial instantiations in the repository as files are recompiled.

Although this option is intended to be used when doing an initial build of a system, it also can be used for ongoing development in a structured way. The user needs to delete or update the timestamp before doing any additional instantiations. Consider the following:


rm cxx_repository/TIMESTAMP 
cxx -ttimestamp -c a.cxx 
cxx -ttimestamp -c b.cxx 
cxx -ttimestamp -c c.cxx 
rm cxx_repository/TIMESTAMP 

The timestamp is removed to ensure that all following compilations generate up-to-date template instantiations. When compiling in the -ttimestamp mode, the compiler creates the timestamp file in the repository if one does not already exist.

All instantiations needed by a.cxx, b.cxx, and a.cxx would be generated, but they would be generated only once, as opposed to the default scheme where they could be generated three times if all three modules used the instantiations.

The user would need to ensure that the timestamp file is removed or touched before any more changes were made to the code base. In the previous example, it is done before compilation and immediately after the compilation of a.cxx, b.cxx, and c.cxx.

When the -ptv command-line option is used, the compiler emits an informational message naming the instantiation and repository file that is being skipped in this mode.

In general, a significant savings of build time using the -ttimestamp automatic template instantiation mode occurs only if the user's code makes heavy use of automatically instantiated templates, and if there is a high degree of sharing of those instantiations among various source files in the application.

5.3 Implicit Inclusion

When implicit inclusion is enabled, the compiler receives permission to assume that if it needs a definition to instantiate a template entity declared in a .h file, it can implicitly include the corresponding .cxx file to get the source code for the definition. For example, if a template entity ABC::f is declared in file xyz.h, and an instantiation of ABC::f is required in a compilation but no definition of ABC::f appears in the source code processed by the compilation, the compiler checks whether a file xyz.cxx exists, and if so processes it as if it were included at the end of the main source file. The preprocessor definitions are whatever would be in place at the point where the file is included. This includes header file guards. So if an implicitly included file includes a file that is guarded and has already been processed in the current compilation, that file is skipped.

Note that if an implicitly included file has already been explicitly included in the program, errors might be reported if the file does not have header guards.

To find the template definition file for a given template entity the front end needs to know the full path name of the file in which the template was declared and whether the file was included using the system include syntax (for example, #include <file.h>). This information is not available for preprocessed source containing #line directives. Consequently, the compiler does not attempt implicit inclusion for source code containing #line directives.

Implicit inclusion works well alongside automatic instantiation, but the two are independent. They can be enabled or disabled independently, and implicit inclusion is still useful when automatic instantiation is not done. Implicit inclusion is turned on by default during normal compilation and turned off by default when compiling with -E or -P.

When implicit inclusion is enabled, it is done even when only preprocessing is being requested, such as with the -E command-line option. This means that some amount of syntactic and semantic processing is being done by the compiler to decide what files need to be implicitly included. This is done to provide complete preprocessor output, so that compiling the preprocessor output is equivalent to compiling the preprocessor input. This behavior is suppressed when implicit inclusion is disabled.

Note that Microsoft compatibility does not automatically disable implicit inclusion. If you want DIGITAL C++ to behave like the Microsoft C++ compiler, you should specify -no_implicit_include on the command line.

When looking for a template definition, DIGITAL C++ uses the following lookup order:

  1. If the #include name for the header file containing the template declaration is specified with an absolute path name, look only in the directory specified by the path name.
  2. If the #include for the header file containing the template declaration is specified with a relative path name, take the following action:

For source files, the appropriate suffixes are, in order of preference: .cxx, .CXX, .C, .cc, .CC, .cpp, and .c, or as defined by the -ptsuf command-line option.

DIGITAL C++ ignores any file extension that does not begin with a dot (.).

The -ptsuf command-line option allows the user to explicitly define the file extensions to be used with implicit inclusion. For example:


 cxx -ptsuf ".CPP.CC" file.cxx 

This command searches for template definition files with the extensions .CPP and .CC.

5.3.1 Compiling Programs with Automatic Instantiation

In general, the use of automatic template instantiation is transparent to the user. Automatic template instantiation is enabled by default. The following commands are equivalent:


 cxx file.cxx 
 
 cxx -pt file.cxx 
 
 cxx -pt -ptr ./cxx_repository file.cxx 

These commands:

The repository to be used can be specified explicitly with the -ptr switch. For example:


 cxx -pt -ptr /project/repository -c file.cxx 

This command compiles file.cxx, produces a file.o in the current directory, and puts instantiated template files in the directory /project/repository.

Multiple -ptr options can be specified on the command line. The first named repository is denoted as the read/write repository. The compiler writes instantiation files to this repository. The other repositories are denoted as read only repositories. They are searched by the link command as described in Section 5.3.2.

DIGITAL C++ attempts to instantiate templates at compile time; therefore, any specialization used in the program must be declared in each file in which the specialization is referenced, to prevent the instantiation of the overridden template function.

5.3.2 Linking Programs with Automatic Instantiation

When all source files are compiled and linked in the same step, the operation is transparent. For example, the following command compiles a.cxx and b.cxx, writes instantiations to ./cxx_repository, and links the application using the object files and the instantiations from ./cxx_repository:


 cxx a.cxx b.cxx 

When compiling and linking an application in separate steps, the repositories that were used during the compilation step must be used in the link step as well. For example, the following command uses ./cxx_repository implicitly in both the compile and link step:


 cxx -c a.cxx b.cxx 
 cxx a.o b.o 

If a repository is explicitly named in the compile step, then it must also be named in the link step. For example:


 cxx -c -ptr my_repository a.cxx b.cxx 
 cxx -ptr my_repository a.o b.o 

It is easiest if the compilation of all sources use the same repository:


 cxx -c -ptr my_repository a.cxx 
 cxx -c -ptr my_repository b.cxx 
 cxx -ptr my_repository a.o b.o 

If different repositories are used for the compilation of the sources, then all repositories must by specified on the link step, as follows:


 cxx -c -ptr repository1 a.cxx 
 cxx -c -ptr repository2 b.cxx 
 cxx -ptr repository1 -ptr repository2 a.o b.o 

At link time, the specified repositories are searched in the order given, to find the required instantiations. If one of the repositories used is the default repository, it must be explicitly specified on the link step if more than one repository is used, as follows:


 cxx -c  a.cxx 
 cxx -c -ptr repository2 b.cxx 
 cxx -ptr ./cxx_repository -ptr repository2 a.o b.o 

It is usually much easier and safer to use a single repository, because the same instantiations could potentially be present in multiple repositories, and it is possible to update some but not all repositories when changes are made to templates.

With manual instantiation, the application link step is relatively straightforward. Objects are linked together in the order in which they appear on the command line. As ld processes each file in turn, it is added to the application. When a library is encountered, only those modules from the library that satisfy a reference for an external symbol that has been previously referenced is loaded. Before moving on to the next file on the command line, ld satisfies all the references that it can, including references from modules in the library. Libraries are processed only once. A file that contains a reference to an external symbol that appears in a library that has already been processed is satisfied only if it has already been linked into the application due to a reference that preceded the completion of the processing of that library.

With automatic instantiation, the process of linking proceeds as described previously for all files specified on the command line. Only the explicitly specified files are linked in this way, and these do not include the template instantiations in the repository. The linker reports a list of unsatisfied externals, which is then processed by the prelinker. For each unsatisfied external, the prelinker attempts to find a file with a .o extension in one of the repositories that satisfy that external. When it finds a .o file that satisfies the external, the prelinker prepends the file to the list of files being linked. This process continues until either there are no unsatisfied externals, or the prelinker is unable to satisfy any missing external symbols.

Note the following:

5.4 Manual Template Instantiation

DIGITAL C++ provides the following methods to instantiate templates manually:


Previous Next Contents Index