Previous | Contents | Index |
DIGITAL C++ provides a mechanism for manual instantiation, using the
#pragma define_template directive. This
directive lets you tell the compiler what class or function template to
instantiate in conjunction with the actual arguments with which the
template is to be instantiated. The #pragma
define_template directive has the following format:
#pragma define_template identifier
<template_arguments>
Identifier is the name of the class or function template that the compiler is directed to instantiate at compile time. For the instantiation to succeed, the definition of the template must appear before the #pragma define_template directive.
Template_arguments is a list of one or more actual types that correspond to the template parameters for the particular class or function template being instantiated. Whatever type is specified is used as the type for the instantiation.
The following is an example of a valid template manual instantiation:
//main.cxx template <class T> void sort (T*); int al[100]; float a2[100]; int main() { sort(a1); sort(a2); return 0; } //sort.cxx template <class T> void sort (T *array) { /* body of sort */ } #pragma define_template sort<int> #pragma define_template sort<float> |
To compile and link these sources, enter the following on the command line:
cxx -nopt main.cxx sort.cxx |
Sorting an array of template class elements requires the use of additional pragmas for the module sort.cxx. For example:
template <class T> void sort (T* array) { /*body of sort*/ } template <class T> class entity { public: T member; int operator < (const entity<T> &) const; } template <class T> int entity<T>::operator < (const entity<T> &operand) const { return member < operand.member; } int al[100]; float a2[100]; entity<int> a3[100]; #pragma define_template sort<int> #pragma define_template sort<float> #pragma define_template sort<entity<int> > void sort_all_arrays () { sort(a1); sort(a2); sort(a3); } |
Note that the define_template pragma is position sensitive. If a define_template occurs lexically before a function, member function, or static data member template definition, the compiler is unable to instantiate the corresponding template because the body of that template is not present before the pragma directive.
The compiler instantiates all instances of sort and of entity::operator< needed for this compilation unit.
To organize a program to use the define_template pragma, you can place the declarations of class and functions templates into header files, and instantiate all instances of a particular template from a single compilation unit. The following example shows how to do this:
// sort.h template <class T> void sort (T*); // entity.h template <class T> class entity { public: T member; int operator < (const entity<T> &) const; }; // main.cxx #include "sort.h" #include "entity.h" int al[100]; float a2[100]; entity<int> a3[100]; int main() { sort(a1); sort(a2); sort(a3); return 0; } // sort.cxx #include "sort.h" #include "entity.h" template <class T> void sort (T* array) { /*body of sort*/ } #pragma define_template sort<int> #pragma define_template sort<float> #pragma define_template sort<entity<int> > |
Compiling the following file provides a definition of entity::operator< with type int:
// entity.cxx #include "entity.h" template <class T> int entity<T>::operator < (const entity<T> &operand) const { return member < operand.member; } #pragma define_template entity<int> |
To compile this example, issue the following command:
cxx main.cxx sort.cxx entity.cxx |
If the program uses other instantiations of entity in other compilation units, you can provide definitions of operator< for those entities by adding define_template pragmas to entity.cxx. For example, if other compilation units use the following instantiations of entity, appending the following pragmas to entity.cxx causes the compiler to generate instantiations of operator< for those requests of entity:
entity<long> and entity< entity<int> >, #pragma define_template entity<long> #pragma define_template entity< entity<int> > |
Note that #pragma define_template is like any other pragma, so that it must appear on a single line. Pragmas may be continued on multiple lines by escaping the end of line with a backslash ( \ ) as with other preprocessor statements.
DIGITAL C++ also provides several other pragmas that provide finer control over the instantiation process. Instantiation pragmas can be used to control the instantiation of specific template entities or sets of template entities. There are two instantiation pragmas:
The argument to the instantiation pragma may be:
a template class name | A<int> |
a template class declaration | class A<int> |
a member function name | A<int>::f |
a static data member name | A<int>::i |
a static data declaration | A<int>::i |
a member function declaration | void A<int>::f(int, char) |
a template function declaration | char* f(int, float) |
A pragma in which the argument is a template class name (for example, A<int> or class A<int> is equivalent to repeating the pragma for each member function and static data member declared in the class. When instantiating an entire class, a given member function or static data member may be excluded using the do_not_instantiate pragma. For example:
#pragma instantiate A<int> #pragma do_not_instantiate A<int>::f |
The template definition of a template entity must be present in the compilation for an instantiation to occur. If an instantiation is explicitly requested by use of the instantiate pragma and no template definition is available or a specific definition is provided, an error is issued.
template <class T> void f1(T); // No body provided template <class T> void g1(T); // No body provided void f1(int) {} // Specific definition void main() { int i; double d; f1(i); f1(d); g1(i); g1(d); } #pragma instantiate void f1(int) // error - specific definition #pragma instantiate void g1(int) // error - no body provided |
The functions f1(double) and g1(double) are not instantiated (because no bodies were supplied) but no errors are produced during the compilation (if no bodies are supplied at link time, a linker error is produced).
A member function name (for example, A<int>::f can be used as a pragma argument only if it refers to a single user-defined member function (that is, not an overloaded function). Compiler-generated functions are not considered, so a name may refer to a user-defined constructor even if a compiler-generated copy constructor of the same name exists. Overloaded member functions can be instantiated by providing the complete member function declaration:
#pragma instantiate char* A<int>::f(int, char*) |
The argument to an instantiation pragma must not be a compiler-generated function, an inline function, or a pure virtual function.
Alternatively, you could use the -define_templates command-line option to instantiate templates. Using the -define_templates option requires the same template definition and compilation procedures as previously described for the define_template pragma.
Considering the previous examples in this section, you can use this qualifier to supply definitions of sort<int>, sort<float>, and sort<entity><int> by compiling the following file using -define_templates:
// sort.cxx #include "sort.h" #include "entity.h" template <class T> static sort (T* array) { /*body of sort*/ } static void function_never_used () { int al[100]; float a2[100]; entity<int> a3[100]; sort(a1); sort(a2); sort(a3); } |
There are also two other command-line options that allow for manual
template instantiation. They are the -tused and -tlocal options. The -tused option acts like -define_templates, except that only those
template instantiations that are referenced in the source file are
actually instantiated. The -tlocal option
acts like -tused, except that the
templates are instantiated with internal linkage. This provides a
simple way to build applications but creates executables that are
larger than necessary. It also fails if the template classes being
instantiated have static data members.
The following sections discuss templates in the context of advanced
program development.
DIGITAL C++ does no dependency management of its own. Because
template instantiations are compiled when source files that reference
those instantiations are compiled, those source files must be
recompiled if the template declaration or definition changes. The -M output from the compiler lists the
implicitly included files, so that the make program can automatically recompile any
source files that depend upon template files. If make is not being used, it is the user's
responsibility to ensure that instantiations that have changed are
recompiled. The user does so by recompiling at least one source file
that references the changed instantiations.
Note that when instantiations are included in libraries, or when
multiple repositories are used, there is a possibility that stale
instantiations may be present. For example, if a template class is used
in a source file, and that template class is modified, the source file
is recompiled and the instantiations that appear in the writable
repository are updated. However, if instances with the same name appear
in objects or libraries that are being linked into the application,
those older instantiations are used in preference to the instantiations
in the repositories.
Also, if a member function of the class that is not referenced in the
recompiled source is referenced from a file that is not recompiled with
the new class definition, some instantiations in the application that
use member functions based on the newer class definition are mixed with
stale instantiations that are based on the older class definition. It
is important to know who is using a template before placing it into a
library or read-only repository, and before modifying it, so that all
affected instances can be kept compatible.
Object files that have been compiled using manual instantiation can be
linked freely with objects that have been compiled using automatic
instantiation. To ensure that the template instantiations needed by the
files compiled with automatic instantiation are provided, the
application must be linked using automatic instantiation, and the
appropriate repositories must be present. When a template instantiation
is present in an explicitly named object file or object library it
takes precedence over the same named instantiation in a repository.
Creating libraries with object files created with automatic
instantiations is relatively straightforward. You must decide where the
instantiations that were generated automatically are provided to the
users of the library. For applications that use the library to link
successfully, all template instantiations that are needed by the code
in the library must be available at link time. Because template
instantiation happens at compile time, the object files that contain
the instantiated templates must be available at link time. This can be
done in two ways:
If possible, it is easiest to put the instantiations in the library.
Note that the instantiations in the library hide the same named
instantiations in any repositories or any libraries following the
library on the command line. This is a good choice if the
instantiations are internal to the library and are not instantiated
directly by the user's code. It is also a good choice if the templates
that are instantiated are stable, and will not be modified by the user.
To put the instantiations in the library, add all of the object files
in the repositories required by the library into the library. For
example, if a library is being made from the source files a.cxx, b.cxx, and
c.cxx, the following code places into the
library all the instantiations necessary along with the specified
object files:
5.5 Advanced Program Development and Templates
5.5.1 Dependency Management
5.5.2 Mixing Automatic and Manual Instantiation
5.5.3 Creating Libraries
cxx -c -ptr lib_repository a.cxx b.cxx c.cxx ar r mylib.a a.o b.o c.o lib_repository/*.o |
If the template instantiations can be overridden by the user, the templates should be provided in a repository that the user specifies after all the user's repositories. For the previous example, create the library as follows:
cxx -c -ptr lib_repository a.cxx b.cxx c.cxx ar r mylib.a a.o b.o c.o |
When linking the application, the user would specify lib_repository as the last read-only repository on the line as follows:
cxx -c -ptr ./cxx_repository -ptr lib_repository user_code.cxx mylib.a |
The user must explicitly name the repository when linking, even if it is the default repository ./cxx_repository; cxx first satisfies all unresolved instantiations from ./cxx_repository, and uses lib_repository to resolve any remaining unresolved instantiations.
Note that only the instantiations that are required by the code in the library are generated in the library repository lib_repository. If you must provide other instantiations that the user requires but cannot instantiate, you must provide these instantiations using manual template instantiation.
If multiple libraries are used and contain the same named
instantiations, the actual instantiation used is the one from the first
library that can satisfy the request.
As shown in the previous example, multiple repositories can be
specified to link an application. The first repository named is the
read-write repository, and when compiling, DIGITAL C++ writes
instantiation object files into it. At link time, all repositories are
read only.
The repositories are searched in a linear order, iteratively, and
satisfy only the unresolved instantiations from each pass. That is,
references from instantiations that are added in one pass are not
resolved until the next pass. Consider the link line in the previous
example:
5.5.4 Multiple Repositories
cxx -c -ptr ./cxx_repository -ptr lib_repository user_code.cxx mylib.a |
In this example, all references that could be resolved from lib_repository would be resolved. Any reference
arising from an instantiation in lib_repository would be resolved by
instantiations in ./cxx_repository.
The following cxx command-line options
are specific to the instantiation of templates:
You might want to disable implicit inclusion with the -ms and -std ms
options to match the behavior on Microsoft C++ more closely.
Specifying this option at link time enables DIGITAL C++ to recognize
and use the template instantiation information files within the
specified repository. If you use this option, make sure that the
repository specified at compile time is the same one specified at link
time.
Note that -tlocal cannot be used in
conjunction with automatic template instantiation. If automatic
instantiation is enabled by default, it is disabled by the -tlocal option. Explicit use of -tlocal and -pt
is an error.
5.6 Command-Line Options for Template Instantiation
-define_templates
Instantiate all template entities declared or referenced in the
compilation unit. For each fully instantiated template class, all its
member functions and static data members are instantiated even if they
were not used. Nonmember template functions are instantiated even if
the only reference was a declaration. Instantiations are created with
external linkage. Overrides -pt at
compile time. Instantiations are placed in the user's .o file.
-tall-Hf
Stops the cxx command after the prelinker
runs, and before the final link. Provided for compatibility with
previous versions of DIGITAL C++. This option is not useful with the
Version 6.0 compiler.
-[no]implicit_include
Enable or disable inclusion of source files as a method of finding
definitions of template entities. Implicit inclusion is enabled by
default, and it and disabled when compiling with -E or -P . The
search rules for finding template definition files are the same as for
include files.
-nopt
Directs the compiler not to instantiate templates automatically.
-nopragma_template
Directs the compiler to ignore any #pragma
define_template directives. This option is provided for
users who want to migrate quickly to automatic instantiation without
having to remove all the pragma directives from their code base.
-pt
Directs the compiler to instantiate templates automatically. This
option is the default. Instantiations are placed in the repository.
-ptr pathname
Specifies a repository, with ./cxx_repository as the default. If you specify
several repositories, only the first is writable, and the rest are read
only. Read-only repositories are used only at link time.
-ptsuf "list"
Specifies a list of file name suffixes that are valid for template
definition files. Items in the list must be separated by commas and
each suffix preceded by a period. A suffix may have no more than eight
characters excluding the beginning period. The default is ".cxx,.CXX,.C,.cc,.CC,.cpp,.c".
-ptv
Turns on verbose or verify mode to display each phase of instantiation
as it occurs. This option is useful as a debugging aid.
-tlocal
Similar to -tused except that the
functions are given internal linkage. This option provides a simple
mechanism for getting started with templates. The compiler instantiates
as local functions the functions used in each compilation unit, and the
program links and runs correctly (barring problems resulting from
multiple copies of local static variables). However, because many
copies of the instantiated functions can be generated, this option
might not be not suitable for production use.
-tused
Instantiate those template entities that were used in the compilation.
This includes all static data members for which there are template
definitions. Overrides -pt at compile
time.
-ttimestamp
Used with automatic instantiation. Causes automatic instantiation to
instantiate templates only if they are already in the repository, or if
the existing instantiations in the repository are older than the
timestamp in the repository.
Previous | Next | Contents | Index |