[an error occurred while processing this directive]

HP OpenVMS Systems

C++ Programming Language
Content starts here HP C++

HP C++
User's Guide for OpenVMS Systems


Previous Contents Index


Chapter 5
Using Templates

A C++ template is a framework for defining a set of classes or functions. The process of instantiation creates a particular class or function of the set by resolving the C++ template with a group of arguments that are themselves types or values. For example:


template <class T> class Array { 
    T *data; 
    int size; 
public: 
    T &operator[](int); 
    /* ...*/ 
};    

The code in this example declares a C++ class template named Array that has two data members named data and size and one subscript operator member function. Array<int> instantiates Array with type int . This instantiation generates the following class definition:


class Array { 
    int *data; 
    int size; 
public: 
    int &operator[](int); 
    /* ...*/ 
};    

The compiler supports instantiation of C++ class, function, and static data member templates. The following sections describe using templates with Version 6.0 compilers or later. To understand the differences between the current compiler and Version 5.n and to migrate from Version 5.n to current compilers, see the appendix on migrating from 5.n compilers.

5.1 Template Instantiation Model

For every template used in a C++ program, the compiler must create an instantiation for that template. How the compiler does this is referred to as the template instantiation model. The template instantiation models differ on the why, what, when, where, and how a template is instantiated. The following outline gives a framework to compare the different models.

  1. Why
    A template can be instantiated implicitly when it is used, manually by specific request, or both.
  2. What (part of the template is instantiated)
    For a template class, each member of the template can be instantiated separately, or if one member is instantiated then all members are instantiated.
  3. When
    Instantiation can occur at compile time or link time. Version 6.0 or later compilers support only compile-time instantiation.
  4. Where
    Templates can be instantiated in the object in which they are referenced or in separate objects that are stored in a repository.
  5. How
    Templates can be instantiated with different linkages. They can be local, global, or COMDAT (I64 only). A COMDAT is like a weak global definition, but in addition to permitting duplicate definitions, the linker attempts to eliminate all of the duplicates, saving space in the image.

The numbers in the preceding list are used in subsequent paragraphs to indicate which aspect of the template instantiation model framework is being referenced.

For complex systems, choosing a template instantiation model is a space, time, and build-complexity issue that can be considered for optimizing build speed and reducing program size. The default model, referred to as automatic template instantiation, is standard-compliant and should work transparently for most users. HP recommends this model.

Automatic template instantiation:

  • Instantiates templates when they are used (1),
  • Instantiates only the pieces of a class that are used (2), and
  • Occurs at compile time (3).

Template instantiation on Alpha and I64 systems differ on the where and the how:

  • On Alpha systems, templates are instantiated in a repository (4) using global linkage (5)
  • On I64 systems, templates are instantiated in the objects that refer to them (4) as COMDATs (5).

The compiler, CXXLINK, and linker all work together to assure that all templates used in a program are instantiated and transparently linked into the final image.

Even when using automatic template instantiation, manual instantiation (1) is also permitted. When using the default model, manually instantiated templates are placed in the object where the manual instantiation occurs (4). On Alpha systems, they have global linkage; on I64 systems, they are COMDATs (5).

See Table 5-1 for a summary of each template instantiation model's What, Where, and How for both implicit and manual instantiation (the "Why").

5.2 Manual Template Instantiation

The compiler provides the following methods to instantiate templates manually:

  • Using the #pragma preprocessor directives.
    Using an instantiation pragma to direct the compiler to instantiate a specific template, as described in Section 5.2.2.
  • Using explicit template instantiation syntax.
    The C++ language now defines specific syntax for specifying that a template should be instantiated. See The C++ Programming Language, 3rd Edition.
    HP strongly recommends using the explicit template instantiation syntax when possible.
  • Using the command-line qualifier method.
    This method directs the compiler to instantiate templates at compile time in the user's object file. Several qualifiers are available to control linkage and extent of template instantiation. For more information about these qualifiers, see Section 5.2.3.

5.2.1 Mixing Automatic and Manual Instantiation

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.

5.2.2 Instantiation Directives

The next sections describe the following instantiatation directives:

#pragma define_template
#pragma instantiate_template
#pragma do_not_instantiate_template

5.2.2.1 #pragma define_template

The compiler 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 
       #include <stdlib.h> 
       template <class T> void sort (T*); 
 
       int al[100]; 
       float a2[100]; 
 
       int main() 
       { 
           sort(a1); 
           sort(a2); 
           return EXIT_SUCCESS; 
       } 
 
       //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 command:


CXXLINK main.cxx,sort.cxx /TEMPLATE_DEFINE=(NOAUTO) 

When you use #pragma define_template or explicit instantiation, only the specified template is instantiated; templates to which it refers because of member types or base classes are not instantiated.

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); 
       } 

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 
       #include <stdlib.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 EXIT_SUCCESS; 
       } 
 
       // 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:


       cxxlink 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> > 

Like any other pragma, the #pragma define_template pragma 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.

5.2.2.2 #pragma instantiate and #pragma do_not_instantiate

The compiler also provides several pragmas that provide fine control over the instantiation process. Instantiation pragmas, for example, can be used to control the instantiation of specific template entities or sets of template entities. There are two instantiation pragmas:

  • The instantiate pragma causes a specified entity to be instantiated, similar to the define_template pragma. It provides finer instantiation control than define_template when instantiating function templates.
  • The do_not_instantiate pragma suppresses the instantiation of a specified entity. It is typically used to suppress the instantiation of an entity for which a specific definition is supplied.

The argument to the instantiation pragma can 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 
        #include <stdlib.h> 
 int main() 
 { 
   int     i; 
   double  d; 
   f1(i); 
   f1(d); 
   g1(i); 
   g1(d); 
          return EXIT_SUCCESS; 
 } 
 #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.

5.2.3 Using Command Qualifiers for Manual Instantiation

Alternatively, you could use the /TEMPLATE_DEFINE qualifier to instantiate templates manually.

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 /TEMPLATE_DEFINE=ALL:


       // 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); 
       } 

You can use the /TEMPLATE_DEFINE=USED and /TEMPLATE_DEFINE=LOCAL qualifiers for manual template instantiation. The /TEMPLATE_DEFINE=USED qualifier acts like /TEMPLATE_DEFINE=ALL, except that only those template instantiations that are referenced in the source file are actually instantiated. The /TEMPLATE_DEFINE=LOCAL qualifier acts like /TEMPLATE_DEFINE=USED, 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.

You can use the /TEMPLATE_DEFINE=ALL_REPOSITORY, /TEMPLATE_DEFINE=USED_REPOSITORY, and /TEMPLATE_DEFINE= IMPLICIT_LOCAL qualifiers to create preinstantiation libraries. See Section 5.6.

5.3 Using Template Object Repositories (ALPHA ONLY)

In automatic template instantiation mode, the compiler attempts to instantiate every referenced template at compile time. 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. You can use implicit inclusion to find it.

The compiler writes instantiation object files to a directory called the repository; file names are based on the names of the entities being instantiated. The default repository is [.cxx_repository] .

5.3.1 Specifying Alternate Repositories

You can use the /REPOSITORY command-line qualifier to specify one or more alternate repository directories. The first repository named is the read-write repository into which the compiler writes instantiation objects when processing. At link time, all repositories are read only. 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.

5.3.2 Reducing Compilation Time with the /TEMPLATE_DEFINE=TIMESTAMP Qualifier

To keep instantiations up to date, the compiler always instantiates templates by default, even if the required template already exists in the respository. However, in environments that share many templates among many sources, this process can increase compilation time.

In these environments, users can specify the /TEMPLATE_DEFINE= TIMESTAMP qualifier to override the default behavior and thereby reduce compilation time. This qualifier causes the compiler to create a timestamp file named TIMESTAMP. in the repository. Thereafter, instantiations are added or regenerated only if needed; that is, if they do not alreay exist, or if existing ones are older than the timestamp.

The /TEMPLATE_DEFINE=TIMESTAMP qualifier is immediately useful when building a system from scratch, starting with an empty repository. It avoids reinstantiating unchanged code and is totally safe, because all required instantiations are generated and up to date.

Incremental application building is normally done without this qualifier, so that new instantiations overwrite earlier ones as sources are recompiled.

Although the /TEMPLATE_DEFINE=TIMESTAMP qualifier is intended mainly for initial builds, you can use it for ongoing development in a structured way. Because the compiler creates a new timestamp file only if one does not already exist, you must remove or modify any existing timestamp file before making changes to your code base. This procedure ensures that all subsequent compilations generate up-to-date instantiations.

In the following example, the file is removed before and immediately after the compilation of a.cxx , b.cxx , and c.cxx .


$ DELETE [.cxx_repository]TIMESTAMP.;* 
$ CXX /TEMPLATE_DEFINE=TIMESTAMP a.cxx 
$ CXX /TEMPLATE_DEFINE=TIMESTAMP b.cxx 
$ CXX /TEMPLATE_DEFINE=TIMESTAMP c.cxx 
$ DELETE [.cxx_repository]TIMESTAMP.;* 

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

Specifying the /TEMPLATE_DEFINE=VERBOSE qualifier causes the compiler to emit an informational message naming the instantiation and repository file being skipped in this mode.

5.3.3 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/TEMPLATE_DEFINE=(AUTO,PRAGMA) file.cxx 
 
CXX/REPOSITORY=[.CXX_REPOSITORY] file.cxx 

These commands:

  • Cause the compilation of the file file.cxx
  • Create any instantiations that are required whose definitions are visible to the compiler
  • Create an executable, a.out , by linking together the generated object file and any instantiations required from the repository

You can specify the repository explicitly with the /REPOSITORY qualifier. For example:


CXX /REPOSITORY=C$:[PROJECT.REPOSITORY] file.cxx 

This command compiles file.cxx , produces an object file in the current directory, and puts instantiated template files in the directory C$:[PROJECT.REPOSITORY].

You can specify multiple directories using the /REPOSITORY qualifier. 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.4.

The compiler 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.

If a template instantiation refers to a static function, that function is created as an external entry point in the primary object file, and the instantiation object file in the repository then refers to this __STF function.

If the template instantiation is linked into an application that does not have the original primary object file, an unresolved reference to the __STF function occurs. If this happens, recompile an object file that regenerates the instantiation or use manual instantiation to reinstantiate the template.

5.3.4 Linking Programs with Automatic Instantiation

When compiling and linking an application, you must use the same repositories in both the compilation and link steps.

If you name a repository explicitly in the compile step, you must also name it in the link step. For example:


CXX /REPOSITORY=[.MY_REPOSITORY] a.cxx,b.cxx 
CXXLINK /REPOSITORY=[.MY_REPOSITORY] a.obj b.obj 

If you use different repositories the compilation of the sources, you must specify all of them on the link step:


CXX /REPOSITORY=[.REPOSITORY1] a.cxx 
CXX /REPOSITORY=[.REPOSITORY2] b.cxx 
CXXLINK /REPOSITORY=([.REPOSITORY1],[.REPOSITORY2]) a.obj b.obj 

At link time, the specified repositories are searched in the order given, to find the required instantiations. If you use several repositories, and if one of them is the default, you must specify all repositories on the link step:


CXX a.cxx 
CXX /REPOSITORY=[.REPOSITORY2] b.cxx 
CXX /REPOSITORY=([.CXX_REPOSITORY],[.REPOSITORY2]) a.obj b.obj 

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.

The CXXLINK step processes object files so that all the external symbol references are resolved. The objects are linked together in the following order:

  1. The order in which object files and object libraries are specified on the command line.
  2. If /NOTEMPLATE_PRELINK is specified, stop.
  3. For each unresolved external, search the repositories in the order specified on the command line for an file that contains that external. If such a file is found, add it at the top of the list of object files being searched.
  4. Link again and repeat Step 3 until no more externals are found or until no more object files are found in which to resolve the external.

Note the following:

  • Instantiations that appear in explicitly linked object files or libraries hide instantiations in the repositories.
  • Only template instantiations that are actually referenced in sources that can instantiate them appear in the repository. You must specify any other instantiations manually or use the /TEMPLATE_DEFINE=ALL_REPOSITORY qualifier.
  • Instantiations are satisfied from the list of unsatisfied externals from the linking of specified files, but are linked at the beginning of those files. This means that they are linked in only if they are satisfied from no specified file, given the linker's file order behavior, and if they bring in any external references they need from the first library that satisfies them.

5.3.5 Creating Libraries

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. This can be done in two ways:

  • Put the instantiations in the library. They hide the same named instantiations in any repositories or any libraries following the library on the command line.
  • Provide a repository that contains the instantiations.

It is usually easiest to put the instantiations in the library. This is a good choice if the instantiations are internal to the library and are not instantiated directly by the user's code. To put the instantiations in the library, add all of the object files in the repositories required by the library into the library as shown in the following example:


CXX /REPOSITORY=[.lib_repository] a.cxx,b.cxx,c.cxx 
LIBRARY/CREATE/OBJECT mylib 
LIBRARY/INSERT/OBJECT mylib a.obj,b.obj,c.obj 
LIBRARY/INSERT/OBJECT mylib [.lib_repository]*.OBJ 

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 /REPOSITORY=[.lib_repository] a.cxx,b.cxx,c.cxx 
LIBRARY/CREATE/OBJECT mylib 
LIBRARY/INSERT/OBJECT mylib a.obj,b.obj,c.obj 

When linking the application, enter the CXXLINK command as follows:


CXXLINK user_code.obj,mylib/LIB 

If some objects from [.lib_repository] are not contained in mylib.olb , specify [.lib_repository] as the last read-only repository on the line follows:


CXXLINK /REPOSITORY=([.cxx_repository],[.lib_repository]) 
        user_code.obj,mylib/LIB 

You 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.

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 you require but cannot instantiate, you must provide these instantiations using manual template instantiation or by specifying the qualifier /TEMPLATE_DEFINE=ALL_REPOSITORY.

5.3.6 Multiple Repositories

As shown in Section 5.5.3, multiple repositories can be specified to link an application. The first repository named is the read-write repository, and when compiling, the compiler 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:


mylib.olb 

In this example, all references that could be resolved from lib_repository would be resolved in the first pass. Any reference arising from an instantiation in lib_repository in the first pass would be resolved by instantiations in [.cxx_repository] in the second pass.

5.4 Using COMDATS (I64 ONLY)

The primary purpose of a template repository is to guarantee that only one copy of a template instantiation is included in a program. Another way to achieve this is to use COMDATs. COMDATs are special symbols that are globally visible; however, when the linker encounters a duplicate definition, it is removed. This allows the compiler to define templates directly in every object module that uses them.

The principal benefit of using COMDATS is build speed, but it also can simplify the build procedure:

  • Compilation speed is improved because writing template instantiations into the current object is significantly faster then writing them into the repository, because of object overhead in the latter case.
  • Link speed is improved because determining which templates to include from the template repository requires multiple passes of the linker.
  • The build is simplified by eliminating the need to manage the template repository and explicitly extract objects.

COMDATs are implemented on I64 systems using ELF group sections. COMDATs are not implemented on Alpha systems because EOBJ does not support them. If EOBJ supported COMDATs, then they also would have been used on Alpha systems instead of the template object repository. Currently, there are no plans to implement COMDATs on Alpha systems.

Because templates instantiated using COMDATs exist in the same object where they are used, there are no special procedures for linking programs or creating libraries, except that a template can only be exported from a single shared library. If two shared libraries with the same exported template are linked together, a MULDEF will occur. This restriction also exists on Alpha systems.

5.5 Advanced Program Development and Templates

The following sections discuss templates in the context of advanced program development.

5.5.1 Implicit Inclusion

When implicit inclusion is enabled, the compiler assumes that if it needs a definition to instantiate a template entity declared in a .h or .hxx file, it can implicitly include the corresponding implementation file to obtain the source code for the definition.

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, the compiler checks whether a file xyz.cxx exists. If it does, the compiler processes it as if it were included at the end of the main source file.

When looking for a template definition, the compiler 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:
    • If the header file name is specified with double quotation marks (" ") and the /NOCURRENT_INCLUDE qualifier was not specified, append the relative path name to the directory containing the source file and search for files with the appropriate suffixes.
    • Otherwise, append the relative path name to all the -I directories and look in those resulting directories for files with the appropriate suffixes.

Note

A place containing a forward slash (/) character is considered to be a UNIX-style name. If the name in the #include directive also contains a "/" character that is not the first character and is not preceded by a an exclamation mark character (!) (that is, it is not an absolute UNIX-style pathname), the name in the #include directive is appended to the named place, separated by a "/" character, before applying the decc$to_vms pathname translation function.

For source files, the appropriate suffixes are, in order of preference: .CXX , .C , .CC , and .CPP or as defined by the /TEMPLATE_DEFINE=DEFINITION_FILE_TYPE qualifier.

The compiler ignores any file extension that does not begin with a dot (.).

The /TEMPLATE_DEFINE=DEFINITION_FILE_TYPE qualifier allows the user to define explicitly the file extensions to be used with implicit inclusion. For example:


CXX file.cxx /TEMPLATE_DEFINE=DEF=".CPP.CC" 

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

5.5.2 Dependency Management

The compiler 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 /MMS output from the compiler lists the implicitly included files, so that the MMS program can automatically recompile any source files that depend upon template files. If MMS 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.

The compiler does not check command line dependencies of template instantiations at link time. If you compile two different source files that instantiate a specific template with two different sets of qualifiers, the behavior is undefined. Use consistent qualifier settings for each build into each repository. Examples of qualifier settings that could cause unexpected results are as follows:

  • /STANDARD=STRICT_ANSI. Use of guiding declarations is not allowed, and some templates might not be instantiated as they would be in other modes.
  • /DEBUG. Debug information is gerenated for some instantiations and not for others. Be sure that is what you want.
  • /NOMEMBER_ALIGNMENT. Some instantiations with this setting assume that classes have unaligned members; instantiations generated by compiling files with the default setting do not.

5.5.3 Creating a Common Instantiation Library

Because the automatic instantiation model has changed to a compile time model with Version 6.0, (see Sections 5.3.3 and 5.3.4), the procedure used to create a common instantiation library has also changed. This section describes the new procedure.

If you want to put all current instantiations into a common instantiation library, follow these steps:

  1. Compile with the /TEMPLATE=VERBOSE qualifier and save the results to a file.
  2. Edit that file and save the names that appear after the "automatically instantiating ..." string. You can ignore any messages about instantiating vtables. Put #pragma instantiate before each name.
  3. Put the result of that edit into a separate source file and include at the top of the file any headers needed for template definitions.
  4. Put matching #pragma do_not_instantiate (see Section 5.2.2.2) into the headers that define each of these template classes or functions.
  5. Place each #pragma do_not_instantiate directive between an #ifndef of the form #ifndef SOME_MACRO_NAME and an #endif .
  6. Compile the inst.cxx file with SOME_MACRO_NAME defined.
  7. Link the source file with the resulting object file.

The following examples show how to create a common instantiation library for all the instantiations currently being automatically instantiated for this file.


// foo.cxx 
#include <stdlib.h> 
#include <vector> 
#include "C.h" 
 
int main() { 
 vector<C> v; 
 v.resize(20); 
        return EXIT_SUCCESS: 
} 
 
// C.h 
#ifndef __C_H 
 
class C {}; 
 
#endif 

Compiling with the /TEMPLATE=VERBOSE qualifier shows which instantiations occur automatically:


void vector<T,Allocator>::resize (size_type new_size) 
..........................^ 
%CXX-I-INSTENTITY, automatically instantiating void 
          std::vector<C, std::allocator<C > 
          >::resize(unsigned int) 
at line number 90 in module VECTOR.CC of text library 
SYS$COMMON:[SYSLIB]CXXL$ANSI_DEF.TLB;4 
// 
  1. Place all these instantiations into a file called inst.cxx that is built separately or into a library:


    // inst.cxx 
    #include <vector> 
    #include "C.h" 
     
    #pragma instantiate void std::vector<C, std::allocator<C > 
     >::resize(unsigned long) 
    #pragma instantiate void std::vector<C, std::allocator<C > 
     >::insert(C *, unsigned long, const C &) 
    #pragma instantiate void std::vector<C, std::allocator<C > 
     >::__insert(C *, unsigned long, const C &, __true_category) 
    #pragma instantiate C *std::copy_backward(C *, C *, C *) 
    #pragma instantiate void std::fill(C *, C *, const C &) 
    #pragma instantiate C *std::copy(C *, C *, C *) 
    #pragma instantiate const unsigned long std::basic_string<char, 
    std::char_traits<char >, std::allocator<void> >::npos 
    
  2. Add these instantiations into C.h and change "instantiate" to "do_not_instantiate". Add an #ifndef , so that when building inst.cxx , the compiler creates these instantiations in the inst object file:


    #ifndef __C_H 
     
    class C {}; 
     
    #ifndef __BUILDING_INSTANTIATIONS 
    #pragma do_not_instantiate void std::vector<C, std::allocator<C > 
     >::resize(unsigned long) 
    #pragma do_not_instantiate void std::vector<C, std::allocator<C > 
     >::insert(C *, unsigned long, const C &) 
    #pragma do_not_instantiate void std::vector<C, std::allocator<C > 
     >::__insert(C *, unsigned long, const C &, __true_category) 
    #pragma do_not_instantiate C *std::copy_backward(C *, C *, C *) 
    #pragma do_not_instantiate void std::fill(C *, C *, const C &) 
    #pragma do_not_instantiate C *std::copy(C *, C *, C *) 
    #pragma do_not_instantiate const unsigned long 
     std::basic_string<char, std::char_traits<char >, 
     std::allocator<void> >::npos 
    #endif 
    #endif 
    
  3. Build the inst object file:


    CXX/DEFINE=BUILDING_INSTANTIATIONS inst.cxx 
    
  4. Link with the inst object file. It will use instantiations from that file instead of creating them automatically:


    cxx foo.cxx 
    cxxlink foo inst 
    

To verify that your procedure worked correctly, you can remove all files from the cxx_repository subdirectory before you compile foo.cxx . This subdirectory should contain no instantiations after linking with the inst object file.

If you have an inst.cxx file that contains many instantiations and you do not want all the symbols in the inst object file to be put into a user's executable even if only some symbols are used, (as happens with archive libraries), you can either split the inst.cxx into many smaller source files, or specify the /DEFINE_TEMPLATE=USED_REPOSITORY qualifier to create the instantiations as separate object files in the repository (see Section 5.6). You must then link all the required individual object files in the repository into your library.

5.6 Command-Line Qualifiers for Template Instantiation

This section describes the C++ command-line qualifiers that specify the template instantiation model, and additional template-related qualifers.

5.6.1 Instantiation Model Qualifiers

The following CXX command-line qualifiers specify the template instantiation model to be used. Specify only one:

/TEMPLATE_DEFINE=ALL

Instantiate all template entities declared or referenced in the compilation unit, including typedefs. 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 /REPOSITORY at compile time. Instantiations are placed in the user's object file.

/TEMPLATE_DEFINE=ALL_REPOSITORY

Instantiate all templates declared or used in the source program and put the object code generated as separate object files in the repository. Instantiations caused by manual instantiation directives are also put in the repository. This is similar to /TEMPLATE_DEFINE=ALL except that explicit instantiations are also put in the repository, rather than than an external symbol being put in the main object file. This qualifier is useful for creating a pre-instantiation library.

/TEMPLATE_DEFINE=[NO]AUTOMATIC

/TEMPLATE_DEFINE=AUTOMATIC directs the compiler to use the automatic instantiation model of C++ templates.

/TEMPLATE_DEFINE=NOAUTOMATIC directs the compiler to not implicitly instantiate templates.

/TEMPLATE_DEFINE=AUTOMATIC is the default.

/TEMPLATE_DEFINE=IMPLICIT_LOCAL

Same as template_define=-local , except manually instantiated templates are place in the repository with external linkage. This is useful for build systems that need to have explicit control of the template instantiation mechanism. This mode can suffer the same limitations as template_define=-local . This mode is the default when -std gnu is specified.

/TEMPLATE_DEFINE=LOCAL

Similar to /TEMPLATE_DEFINE=USED except that the functions are given internal linkage. This qualifier 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 qualifier might not be not suitable for production use.

The /TEMPLATE_DEFINE=LOCAL qualifier cannot be used in conjunction with automatic template instantiation. If automatic instantiation is enabled by default, it is disabled by the /TEMPLATE_DEFINE=LOCAL qualifier. Explicit use of /TEMPLATE_DEFINE=LOCAL and /PT is an error.

/TEMPLATE_DEFINE=USED

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.

/TEMPLATE_DEFINE=USED_REPOSITORY

Like /TEMPLATE_DEFINE=ALL_REPOSITORY, but instantiates only templates used by the compilation. The explicit instantiations are also put into the repository as separate object files.

Table 5-1 summarizes each template instantiation model's What, Where, and How (as described in Section 5.1) for both implicit and manual instantiation.

Table 5-1 Template Instantiation Models
  Why
  Implicit Manual
Model (/TEMPLATE=) What Where How What Where How
AUTO part repository for Alpha, object for I64 global for Alpha, COMDAT for I64 part object global for Alpha, COMDAT for I64
NOAUTO N/A N/A N/A part object global for Alpha, COMDAT for I64
IMPLICIT_LOCAL part object local for Alpha, COMDAT for I64 part object global for Alpha, COMDAT for I64
LOCAL part object local for Alpha, COMDAT for I64 part object local for Alpha, COMDAT for I64
USED part object global for Alpha, COMDAT for I64 part object global for Alpha, COMDAT for I64
USED_REPO part repository global for Alpha, COMDAT for I64 part repository global for Alpha, COMDAT for I64
ALL all object global for Alpha, COMDAT for I64 all object global for Alpha, COMDAT for I64
ALL_REPO all repository for Alpha, object for I64 global for Alpha, COMDAT for I64 all repository for Alpha, object for I64 global for Alpha, COMDAT for I64

5.6.2 Other Instantiation Qualifiers

The following qualifiers are independent of the model used and each other:

/TEMPLATE_DEFINE=DEFINITION_FILE_TYPE="file-type-list"

Specifies a string that contains a list of file types that are valid for template definition files. Items in the list must be separated by commas and preceded by a period. A type is not allowed to exceed the OpenVMS limit of 31 characters. This qualifier is applicable only when automatic instantiation has been specified. The default is /TEMPLATE_DEFINE=DEF=".CXX,.C,.CC,.CPP".

/TEMPLATE_DEFINE=PRAGMA

Determines whether the compiler ignores #pragma define_template directives encountered during the compilation. This qualifier lets you quickly switch to automatic instantiation without having to remove all the pragma directives from your program's code base.The default is /TEMPLATE_DEFINE=PRAGMA, which enables #pragma define_template .

/TEMPLATE_DEFINE=VERBOSE

Turns on verbose or verify mode to display each phase of instantiation as it occurs. During the compilation phase, informational level diagnostics are generated to indicate which templates are automatically being instantiated. This qualifier is useful as a debugging aid.

/PENDING_INSTANTIATIONS[=n]

Limit the depth of recursive instantiations so that infinite instantiation loops can be detected before some resource is exhausted. The /PENDING_INSTANTIATIONS qualifier requires a positive non-zero value n as argument and issues an error when n instantiations are pending for the same class template. The default value forn is 64.

5.6.3 Repository Qualifiers

The following qualifiers are only applicable if a repository is being used (ALPHA ONLY):

/TEMPLATE_DEFINE=TIMESTAMP

Causes the compiler to create a timestamp file named TIMESTAMP. in the repository. Thereafter, instantiations are added or regenerated only if needed; that is, if they do not alreay exist, or if existing ones are older than the timestamp.

/REPOSITORY

Specifies a repository that C++ uses to store requested template instantiations. The default is /REPOSITORY=[.CXX_REPOSITORY]. If multiple repositories are specified, only the first is considered writable, and the default repository is ignored unless specified.


Previous Next Contents Index