United States    
COMPAQ
C++
Compaq C++

Compaq C++
Using Compaq C++ for OpenVMS VAX


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

Compaq C++ supports instantiation of C++ class, function, and static data member templates. The following sections describe the alternative methods available for instantiating templates.

Automatic Instantiation

This method instantiates templates automatically, as they are needed by your application. Special conventions must be followed regarding the name and location of template declaration and definition files. Additionally, to successfully link your application you must use the CXXLINK facility to ensure that all requested instantiations are included in the resulting application's executable. To get started quickly using automatic instantiation, see Section 5.1. For details, see Section 5.2.

Manual Instantiation

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

5.1 Automatic Instantiation Quick Start

When invoked, Compaq C++ does not instantiate any template requests made by the application at compile time. Instead, it creates instantiation source files in a repository. The repository is a directory where Compaq C++ stores various information about the templates being instantiated. The files in the repository contain the source used to generate the requested instantiations. Consider the following example:


 
//main.cxx 
#include "Array.h"          //template declaration file 
#include <String.hxx>       //argument declaration file 
 
int main() 
{ 
    Array<String> a; 
 
    a[0] = "hello world";    //request of Array's subscript 
                             //operator 
    return 0; 
} 
 

The header file Array.h is referred to as the template declaration file. To organize a program to use automatic instantiation effectively, you must place the declarations of class and function templates into header files. For more information, see Section 5.2.4.

The contents of Array.h are as follows:


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

The definitions or bodies of function and class member function templates should appear in a separate header file referred to as the template definition file. For more information regarding the contents of template definition files, see Section 5.2.5.

The template definition file should have the same base name as its corresponding template declaration file but with a different file type. In the current example, the template definition file is named Array.cxx and is located in the same directory as Array.h . The contents of Array.cxx are as follows:


 
template <class T> T & Array<T>::operator[](int i) { 
    return *(data + i); 
} 
 

For more details on template definition file lookup rules, see Section 5.2.7.1.

In the previous example of main.cxx , String is the type used to instantiate the class template Array . Declarations of various types that are used to instantiate a particular template must be defined within a header file. A compile-time diagnostic will be issued if Compaq C++ compiles an instantiation whose argument type was defined in the source file instead of in a header file. For example:


//error.cxx 
#include "Array.h" 
 
struct MyString {  
    /* ...*/ 
}; 
 
int main() 
{ 
    Array<MyString> a; 
 
    a[0] = "Hello World"; 
    return 0; 
} 

In this example, an error occurs because Compaq C++ does not duplicate the definition of MyString in any instantiation source file created as a result of compiling the source file error.cxx .

Because the Compaq C++ compiler performs automatic template instantiation by default, you need not specify the /template_define=auto qualifier on the command line. To compile the main program, use a cxx command such as the following:


cxx/include=[.include] main.cxx 

This command assumes that main.cxx is located in the current directory and Array.h and Array.cxx are in the sys$disk:[.include] subdirectory.

Compaq C++ creates the default repository in sys$disk:[.cxx_repository] . For more details about the contents of repositories, see Section 5.2.3.

One of the instantiation requests needed by the source file main.cxx is an instantiation of the Array object's subscript member function, Array<T>::operator[](int) , with type String . To satisfy this request, Compaq C++ will generate the following instantiation source file in the default repository:


 
#include "Array.h"       //template declaration file 
#include <String.hxx>    //argument declaration file 
#include "Array.cxx"     //template definition file 
 
typedef Array<String> __dummy_; 
 

To link the main program, use the cxxlink command. For example:


cxxlink main.obj 

The CXXLINK facility is layered on top of the OpenVMS Linker utility and provides the ability to link your C++ application. This facility determines the instantiation requests generated by the application. For each request, CXXLINK reinvokes Compaq C++ in a special template request mode to compile the necessary instantiation. The resulting object files are stored in a repository for use when CXXLINK links the application.

The following are some other helpful notes to get started quickly:

5.2 Automatic Instantiation Specifics

This section describes automatic instantiation in detail, beginning with an overview of the instantiation process.

5.2.1 Overview of the Instantiation Process

The process of automatically instantiating a template has the following steps:

  1. The user issues a cxx command to compile specified files.
  2. Compaq C++ sets the current repository. If the user specifies the /repository option and the specified repository does not exist, Compaq C++ creates the user-specified repository. Otherwise, Compaq C++ checks for the existence of a repository named sys$disk:[.cxx_repository] . If sys$disk:[.cxx_repository] does not exist, Compaq C++ creates one in your current working directory.
  3. Compaq C++ invokes the compiler in default mode. The compiler uses the template declaration file, the template definition file, and the top-level header files of your application to create an instantiation source file for each function template or class template used within the source program.
    If an instantiation source file already exists in the repository, the compiler checks whether this file is current with regard to the corresponding header files and the command-line options. If the instantiation source file is out of date, the compiler re-creates it.
  4. Compaq C++ completes the compilation and returns control to the user.
  5. The user issues a cxxlink command to link the object files resulting from the previous steps, plus any object or shared library files. If you explicitly specified the repository path name during compilation, you must also specify the repository path name to the cxxlink command.
  6. If the user does not specify the /notemplate_prelink qualifier on the cxxlink command, CXXLINK performs a prelink to determine the unresolved symbols. This prelink does not create an executable image; CXXLINK directs the OpenVMS Linker utility to perform only the symbol resolution.
  7. For each unresolved symbol reported by the linker in step 6, Compaq C++ searches either the default or the user-specified repositories for the corresponding instantiation object file. The writeable repository is searched first, followed by any read-only repositories specified on the cxxlink command.
  8. For each instantiation object file found in the writeable repository, Compaq C++ checks whether that file is up-to-date with regard to the instantiation source file. For class templates, Compaq C++ also checks the contents of the instantiation request file to see whether it specifies all the members that have been requested thus far during the prelink phase. If the instantiation object file is found to be out of date or the instantiation request file is missing any requests, Compaq C++ invokes the compiler in template request mode to generate the instantiation. In template request mode, the compiler generates code only for the function template or class template members specified in the instantiation request file. The resulting object file is placed in the writeable repository and added to the link command in step 6. If the instantiation object file is up-to-date, Compaq C++ simply adds the file to the link command in step 6.
    For each instantiation object file found in a read-only repository, Compaq C++ performs no dependency checking except for class templates: Compaq C++ verifies that the instantiation request file specifies all the members that have been requested thus far during the prelink phase. If the instantiation request file is missing any requests, Compaq C++ ignores the object file. Otherwise Compaq C++ adds the object file to the link command in step 6.
    If no instantiation object file is found, or if a read-only repository object was found but is missing requests, Compaq C++ creates an instantiation object file by compiling (in template request mode) the instantiation source file located in the writeable repository. The resulting object is placed in the writeable repository and added to the link command in step 6.
  9. CXXLINK then repeats steps 6 to 8 until either no unresolved symbols remain or no object files can be found within the specified repositories that correspond to the unresolved symbols.
  10. To perform the final link, CXXLINK invokes the OpenVMS Linker utility with the modified link command.

5.2.2 Compiling and Linking

The following Compaq C++ cxx command-line qualifiers support automatic instantiation of C++ templates:

/[no]template_define[=(option,...)]

Controls compiler behavior pertaining to the instantiation of C++ templates. The options are as follows:
Option Usage
[no]automatic Directs Compaq C++ to turn on or off automatic instantiation of C++ templates. The default is
/template_define=automatic .
[no]all Instantiates all function templates that are used and all member functions and static data members of any class templates that are used during this compilation. For /template_define=all , the template definition must be present before the point of each instantiation in the source file. The default is /template_define=noall .

Note that compile-time instantiation is generally not practical for larger applications. Template entities are instantiated with external linkage when /template_define=all is in effect, which results in multiply defined symbol errors at link time if the same template entities instantiated in different modules.

local Instantiate only the template entities that are used in this compilation, and force those entities to be local to this compilation. The default is /template_define=nolocal .
used Instantiate only the template entities that are used in this compilation. The template entities are instantiated with external linkage. The default is /template_define=noused .
[no]pragma Determines whether Compaq C++ ignores
#pragma define_template directives encountered during the compilation. This option 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 .
definition_file_type= 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 file type is not allowed to exceed the OpenVMS limit of 39 characters. This option is applicable only when automatic instantiation has been specified. The default is /template_define=definition_file=
".CXX,.C,.CC,.CPP"
.

To preserve compatability with releases prior to Version 5.3, the behavior is unchanged if you specify /template_define or /notemplate_define without options. Thus:
Qualifier Default for Options
/template_define /template_define= -
(noauto,all,nolocal,noused,pragma)
/notemplate_define /template_define= -
(noauto,noall,nolocal,noused,pragma)

If you do not specify /template_define or /notemplate_define , the default is to perform automatic instantiation. Thus:
Qualifier Default for Options
None specified. /template_define= -
(auto, -
noall, -
nolocal, -
noused, -
pragma, -
definition_file_type=".CXX,.C,.CC,.CPP")

If you specify /template_define with some options, the defaults for non-specified options are the same as previously listed. For example, if you specify /template_define=definition_file_type=".CXX" , the options in effect are as follows:


/template_define= - 
(automatic,noall,nolocal,noused,pragma,definition_file_type=".CXX") 

/repository=(pathname [,...])

Specifies a repository that Compaq C++ will use to store requested template instantiations. The default is /repository=[.cxx_repository] . If multiple repositories are specified, only the first is considered writeable and the rest are read only.

You must link your application using the cxxlink command. For details on using CXXLINK, see Section 1.3.

5.2.3 Repositories

Between distinct compilations and linking, Compaq C++ stores the necessary template instantiation information in a repository. You can specify a path name for the repository at both compile time and link time using the /repository command-line qualifier. If you specify multiple path names, the first becomes the writeable repository and the rest are designated as read-only repositories. A repository is implemented as a OpenVMS directory that can contain the following:

For example, given an instantiation request for the member function element(T) of template class Vector<int> , the repository would contain the following files:
File Description
Vector__Ti.com Dependency file containing the command-line options
Vector__Ti.cxx Instantiation source file
Vector__Ti.mms Dependency file containing the list of header files
Vector__Ti.obj Instantiation object file
Vector__Ti.req Instantiation request file

Repository Permissions

The default repository acquires the same permissions as the directory in which it resides, and therefore has the same access as the parent directory. Any files created in the repository reflect this access.

5.2.4 Template Declaration File

This file contains the declaration of the template; include it in your application using the #include syntax.

The following is an example of a declaration file, AAA.h , containing a class template:


#ifndef AAA_H 
#define AAA_H 
template <class T> class AAA 
{ 
    T x; 
    int y; 
public: 
    void f(void); 
    void g(T&); 
}; 
#endif 

The following is an example of a forward declaration for a function template:


template <class T> void sort (T*, int n); 

A template declaration file should include the header files it needs for the types it uses. It should not include header files for types used as template arguments or the definition of the template itself because Compaq C++ handles these automatically.

5.2.5 Template Definition File

This file contains the template implementation. Given the previous declaration file example, AAA.h , the corresponding template definition file, AAA.cxx would contain code fragments as follows:


template <class T> void AAA <T>::f(void) { /* ...*/ } 
   .
   .
   .
template <class T> void AAA <T>::g(T&) { /* ...*/ } 

Template definition files are treated like header files. They should contain only definitions of function, member function, or static data member templates. To avoid external linkages leading to unresolved symbols, Compaq C++ ignores any definitions of functions or data items found in template definition files.

If you include the corresponding declaration file in the template definition file, or include the argument files that declare any template argument types, you should guard against multiple inclusion. If a definition file does not exist for each corresponding declaration file, you must write a name-mapping file to override the standard convention (see Section 5.2.6). Otherwise, Compaq C++ does not include the definition file in the instantiation source file.

5.2.6 Name-Mapping File

The name-mapping file is supplied by the user and must be named Template.dat . The file is in the repository directory and contains information the compiler uses to automatically instantiate templates; it supplements template information found in the source code and in header files.

A name-mapping file consists of the following entries:

Compaq C++ permits only one definition entry per template name. Thus, if the same name is used to define three different templates, each within its own file, you must list all three files in a single definition entry for that name.

Example 1

Consider the following template function:


template <class T> void sort( T *p ) {} 

The following is a valid name-mapping file definition entry:


definition sort in "sort_impl.cxx"; 

With this definition, the compiler expects to find the body of function template sort() in the source file sort_impl.cxx .

Example 2

Consider the following template member function:


   template <class T> class List { 
   public: 
        void add_item(T new_item); 
   /* ...*/ 
   }; 

The following is a valid name-mapping file definition entry:


   definition add_item in "List_funcs.cxx"; 

This definition instructs the compiler to find the body of the member function template add_item() in the source file List_funcs.cxx .

Presumably the source file List_funcs.cxx contains the following code:


#include "List.h" 
template <class T> void List<T>::add_item(T new_item){} 

Automatic instantiation makes including List.h more than once a possibility. To guard against this happening, you should enclose the code in include guards, as in the following example:


#ifndef List_H 
#define List_H 
... // code for List.h 
#endif 

Example 3

Consider the following overloaded template member function:


//List.h 
#ifndef List_H 
#define List_H 
template <class T> class List { 
Public: 
   List(); 
   List(const List& l); 
   /* ...*/ 
}; 
#endif 
 
//List.cxx 
#include "List.h" 
template <class T> List<T>::List(){} 
 
//List_copy.cxx 
#include "List.h" 
template <class T> List<T>::List(const List& l){} 

The following is a valid name-mapping file definition entry:


definition List in "List.cxx", "List_copy.cxx"; 

5.2.7 Instantiation Source File

By default, Compaq C++ creates one instantiation source file for each function template instantiation request, and one for each class template instantiation request. The instantiation source file includes all the headers needed to correctly instantiate a template. The order of inclusion is as follows (no file is included more than once):

  1. Template declaration file and top-level header files, in the order they appeared in the compilation unit requesting the instantiation
  2. Template definition files

For example, consider the following name-mapping file:


# Stack Implementation 
definition Stack in "Stack_Impl.cxx"; 

With an external unresolved symbol, Stack<C>::push(C) , where class C is defined in the header file Cdef.h , the result would be the following instantiation source file:


#include "Cdef.h" 
#include "Stack.h" 
#include "Stack_Impl.cxx" 
 
typedef Stack<C> __dummy_; 

5.2.7.1 Template Definition File Lookup

Compaq C++ matches each instantiation request with the corresponding template definition file to create the instantiation source file. The name of this source file must be similar to that of the template declaration file. Compaq C++ uses the following lookup order:

  1. Search the user-provided name-mapping file ( Template.dat , in the repository list) for the name of the function, member function, or static data member template.
  2. 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.
  3. If the #include name for the header file containing the template declaration is specified with a relative path name, take the following action:
    1. If the header file name is specified with double quotation marks (" ") and the /nested_include_directory=none option was not specified, append the relative path name to the directory containing the source file, and search for files with the appropriate suffixes.
    2. Otherwise, append the relative path name to all the
      /include_directory directories and look in those resulting directories for files with the appropriate suffixes.

    For source files the appropriate suffixes are, in order of preference: .CXX , .C , .CC , and .CPP , or as defined by the command-line option
    /template_define=definition_file_type .
    Compaq C++ ignores any file types that do not begin with a dot or that have more than 39 total characters in the extension.

5.2.8 Dependency Management

When a program makes a template instantiation request, Compaq C++ avoids creating and compiling a new instantiation source file if existing source and object files can be reused.

For each instantiation request, when Compaq C++ finds a corresponding instantiation source file or object file in the writeable repository, the following checks are performed:

  1. Compaq C++ compares the command-line options used to compile the existing instantiation source file with the current command-line options. If the options do not match, then Compaq C++ creates and compiles a new instantiation source file.
  2. The creation date of the instantiation object file is compared to the modification date of each header file included by the instantiation source file. If any header files have changed since the object file was created, then Compaq C++ creates and compiles a new instantiation source file.
  3. For class templates, Compaq C++ examines the contents of the instantiation request file to determine if any requests are missing. If the instantiation request file is not complete, Compaq C++ modifies the file and then recompiles the instantiation source file.

5.2.9 Building Libraries and Applications That Use Templates

When you build libraries and applications using manual instantiation, you face the tedious tasks of ensuring that all necessary templates are instantiated and of determining where each template instantiation is generated to avoid duplicates. The advantage of this method is that you have complete control over template instantiations; however using this approach can be time-consuming.

Automatic template instantiation performs these manual tasks for you if your build procedure and sources are properly set up. The procedure for building libraries and applications using automatic template instantiation differs somewhat from the procedure for manual instantiation. This section discusses the basic issues when using automatic template instantiation; it also provides suggestions and examples for resolving these issues.

See Section 3.4 for suggestions on organizing your sources.

5.2.9.1 Building a Standalone Library

Library vendors obviously need to provide a standalone library to their customers. With respect to templates, standalone means that the library contains:

In addition to library vendors, developers of large applications may want to divide their application into one or more standalone libraries to facilitate building. Creating a single library is straightforward. However, creating multiple libraries is more difficult if the libraries use the same template instantiations.

5.2.9.1.1 Creating a Single Library


Before Compaq C++ added support for automatic template instantiation, a typical build procedure would compile all the library sources and place the resulting object files into an object library. Template instantiations were generated through use of the /template_define[=all] option or through use of explicit instantiation or #pragma define_template statements within the sources.

When you use automatic template instantiation, you should compile all the library sources and partially link them; then, place the resulting object files into an object library. First, examine your sources and determine whether or not you need the explicit instantiation statements. Then, modify your build procedure to specify the appropriate cxx and cxxlink commands. See the following paragraphs for details.

Your Library Sources

An important similarity between manual and automatic template instantiation is that only the requested templates are instantiated. A requested template is one that is either of the following:

Therefore, when you examine your library sources, consider the following:

Building Your Library Sources

Each library source file needs to be compiled and partially linked; this means each file will have a cxx and cxxlink command in your build procedure. Optionally, instead of having multiple cxxlink commands, you can use a single cxxlink command that specifies all of the library object files.

You must specify the following option on your cxx and cxxlink commands:

Do not specify the /template_define[=all] option on your cxx command.

You should specify the following option on your cxxlink command:

A Sample Build Procedure

Suppose you want to create a standalone library of window routines and your build directories look like the following:


                 windows_library 
                        | 
        +---------------+-------------------+ 
        |               |                   | 
       src            build              include 
        w1.cxx          |                  template1.hxx 
        w2.cxx        repository           template1.cxx 
        ......

Your build procedure, build_libwindows.com , would be as follows:


$ define/trans=concealed lroot disk:[windows_library.] 
$ define lbuild lroot:[build] 
$! 
$! Compile and partially link each source separately using the same repository: 
$! 
$!   /repository option causes all template instantiation files and objects 
$!      to be placed in the [.build.repository] directory; you must specify the 
$!      same repository for each cxx and cxxlink command 
$! 
$!   /object option causes the source object files to be placed in the build 
$!      directory 
$! 
$!   cxxlink/noexe commands ensure that both the source and template 
$!      instantiations are compiled but no executable is generated 
$! 
$ cxx lroot:[src]w1.cxx /include=lroot:[include] - 
  /repos=lroot:[build.repository] /object=lbuild:w1.obj 
$ cxxlink/noexe lbuild:w1.obj /repos=lroot:[build.repository] 
$! 
$ cxx lroot:[src]w2.cxx /include=lroot:[include] - 
  /repos=lroot:[build.repository] /object=lbuild:w2.obj 
$ cxxlink/noexe lbuild:w2.obj /repos=lroot:[build.repository] 
$! 
$! ... and so on for all library source files 
$! 
$! And now place all the source objects and template instantiation objects 
$! into the object library 
$! 
$ lib/create lbuild:libwindows.olb lbuild:*.obj,lroot:[build.repository]*.obj 

If you are a library provider, you may wish to create a shareable library from the resulting object library and provide one or both libraries to your customers. As noted in Section 3.4, you must also provide the template declaration and template definition files for any templates that your customers may use directly. You do not need to provide template declaration and definition files for templates that are used solely by your library sources.

5.2.9.1.2 Creating Multiple Libraries


If there are no common template instantiations among the libraries, then you can follow the directions in Section 5.2.9.1.1 to build each library.

If the libraries do share template instantiations, you must avoid creating the same instantiations in multiple libraries. Otherwise, any applications that link against your libraries will encounter multiply defined symbols. To solve this problem, you need to create another library that will contain the common instantiations.

No automated way exists to determine the set of common instantiations needed by your libraries. One method is to first build each library following the directions in Section 5.2.9.1.1, then compare the list of object files from each library's repository and select the duplicates to place in your common instantiation library. Another method is to build each library and then link them together using the /include=(module1, module2, ...) option on your link command. The resulting multiply defined symbols should be placed in your common instantiation library. For details on building this common instantiation library, see Section 5.2.9.3.

5.2.9.1.3 Using the /log Option on Your cxxlink Command


When you specify the /log option on your cxxlink command, CXXLINK displays informational messages about each unresolved symbol, each instantiation object file found, and each compilation of an instantiation source file.

These messages can be a useful debugging tool when you are trying to determine which template instantiations are reused, created, or not found. You may see Unable to access file messages. Remember that during the prelink phase, CXXLINK searches the repositories for corresponding files for every unresolved symbol and displays the access message for any file that is not found. Thus, instead of watching for these access messages, you can examine the results of the final link or final partial link to determine whether you have unresolved symbols.

Typically, unresolved symbols indicate that an external library or object file is missing from your cxxlink command. Unresolved symbols are also caused when a source file or instantiation source file cannot be compiled, so examine your build results for compilation errors. If you cannot determine the cause of an unresolved symbol, refer to Section 3.4 for an explanation of how to organize your code to ensure that the symbol is generated.

5.2.9.2 Building an Application

Whether you have a large or small application, whether you have one or multiple source directories, remember to use the same repository for all components that are accumulated into any one executable. This is important because it ensures that no duplicate instantiations are created or accumulated into the executable, and it provides the best build performance.

If you previously relied on manual instantiation to build your application, you should no longer use the /template_define[=all] option on your cxx command nor make explicit instantiation requests within your sources. The automatic instantiation process guarantees that all needed template instantiations will be generated. Otherwise, unless you maintain the manual instantiation requests, they may generate unnecessary instantiations as your sources change.

5.2.9.2.1 Building From Multiple Source Directories


Large applications are often divided into multiple components because building each component individually is easier to manage than building the whole. Typically each component is built by compiling all the component's source files, and then once all the components are built, the resulting object files are linked together to create the final executable.

This basic method applies when you are using automatic template instantiation, but you have the added concern of ensuring that template instantiations are generated and linked into the final executable.

Building Your Application Sources

You can continue to compile your application sources separately and then link them together as the last step.

You must specify the following option on your cxx commands:

You must specify the following option on your cxx and cxxlink commands:

A Sample Build Procedure

Suppose you have a spreadsheet application that is separated into three major components: user interface, reporting, and computing. These components are built individually and then accumulated along with the main program into a single executable.

Your build directories look like the following, where each component has its own subdirectory for sources, includes, and building in addition to the final build directory:


                         spreadsheet_proj 
                                 | 
         ---------------------------------------------------------- 
         |                       |             |        |         | 
         ui                     rpt          comp     main      final_build 
         |                       |             |        |         | 
   ----------------       --------------      ...    main.cxx   repository 
   |      |       |       |      |     | 
  src   build  include   src   build include 

You would have one build procedure for each component as well as a final build procedure. What is important is that each build procedure, including the final build, uses the same /repository option. The ui build procedure, build_ui.com , would look like the following, and the other component procedures would be similar:


$ define/trans=concealed uiroot disk:[spreadsheet_proj.ui.] 
$ define uibuild uiroot:[build] 
$ define/trans=concealed firoot disk:[spreadsheet_proj.final_build.] 
$! 
$! Compile each source separately 
$! 
$!   /repository option causes all template instantiation files to be placed 
$!      in the [.final_build.repository] directory; you must specify the same 
$!      repository for each cxx command 
$! 
$!   /include option specifies the full pathname so that later compilation of 
$!      the template instantiations will succeed 
$! 
$!   /object option causes the source object files to be placed in the 
$!      [.ui.build] directory 
$! 
$ cxx uiroot:[src]ui1.cxx /include=uiroot:[include] /repos=firoot:[repository] - 
    /object=uibuild:ui1.obj 
$! 
$! ... and so on for each source in this component 
$! 
$! Lastly, accumulate all the component object files into an object library 
$! 
$ lib/create uibuild:libui.olb uibuild:*.obj 

Your final build procedure, build_spreadsheet.com , would look like the following:


$ define/trans=concealed sroot disk:[spreadsheet_proj.] 
$ define uibuild sroot:[ui.build] 
$ define uiinclude sroot:[ui.include] 
$ define rptbuild sroot:[rpt.build] 
$ define rptinclude sroot:[rpt.include] 
$ define compbuild sroot:[comp.build] 
$ define compinclude sroot:[comp.include] 
$ define/trans=concealed firoot disk:[spreadsheet_proj.final_build.] 
$! 
$! Compile the main program and create the final executable 
$! 
$!    /repository option must specify the same repository used by the component 
$!             builds 
$! 
$!    all objects from component builds are included in the final executable; 
$!    the template instantiation objects are created during the prelink phase 
$!    and are included in the final executable 
$! 
$ cxx sroot:[main]main.cxx /include=(uiinclude, compinclude, rptinclude) - 
  /repos=firoot:[repository] /object=sroot:[main]main.obj 
$ cxxlink sroot:[main]main.obj, uibuild:libui/lib, rptbuild:librpt/lib, - 
  compbuild:libcomp/lib /repos=firoot:[repository] - 
  /exe=sroot:[final_build]spreadsheet.exe 

5.2.9.2.2 Building Against a Standalone Library


Your application usually needs to link against external libraries. When an external library contains template instantiations, your application needs access to the external object (or shareable) library, as well as to any template declaration and template definition files provided with the library.

To ensure that your application uses the instantiations provided by a particular library, instead of re-creating the instantiations, do the following:

Using the previous spreadsheet example and assuming the library's include directory is library_include and the object library exists in library_build:libwindows.olb , the following changes are necessary:

5.2.9.3 Building a Common Instantiation Library

One way to speed up your build process is to create a reusable library of the stable, common instantiations used by your program. Examples of stable instantiations are STL templates instantiated with built-in types. You can create such a library by producing one or more source files containing explicit instantiation requests for each instantiation you want in the library. Then, build the source files, place the resulting object files in a library, and specify the library during the prelink or final link of your program.

For example, suppose you wanted instantiations of the STL vector class with types int , char , and const char* . You could create a file called vector_inst.cxx as follows:


 
#include <vector> 
 
template vector<int>; 
template vector<char>; 
template vector<const char*>; 
 

This example assumes that the header file <vector> contains the definitions of all necessary instantiations. If it does not, you must either include the template definition file in vector_inst.cxx directly or modify vector_inst.cxx to create actual vector objects, as follows:


// file vector_inst.cxx 
 
#include <vector> 
 
void __use_vector() { 
        vector<int> v1; 
        vector<char> v2; 
        vector<const char*> v3; 
} 
 

Then, compile and partially link this file using the same commands as described in Section 5.2.9.1.1:


cxx/assume=noheader_type_default/repos=[.lib_repository] vector_inst.cxx 
cxxlink/noexec/repository=[.lib_repository] vector_inst 

In this example, the cxx command compiles vector_inst.cxx and creates instantiation source files for any needed instantiations. The cxxlink command compiles those instantiations and generates any further instantiations that are needed. The object file vector_inst.obj will be in the current directory. If additional instantiations were generated, their object files will be in the repository [.lib_repository] .

You can then use the following command to create an object library called libinst.olb :


lib/create libinst.olb vector_inst.obj,[.lib_repository]*.obj 

For the C++ Standard Library, Compaq provides the following command procedure:


sys$common:[syshlp.examples.cxx]build_common_instantiation_library.com 

This procedure automatically creates a library of common instantiations for the STL containers and other class templates. You can examine this as an example of what the source files should contain and how to create the library.

5.3 Useful Conventions

This section describes ways to structure and name an application's files to make the best use of automated template instantiation.

5.3.1 Inline Functions

You can declare and define an inline member function within a class template definition. For example:


template <class T> class List { 
public: 
    List() {} 
    /* ...*/ 
}; 

You can also define it in the template definition file. For example:


template <class T> inline List<T>::List() {} 

The inline keyword is required if the inline function is defined outside the class template definition; otherwise the member is not inlined.

5.3.2 Specializations

To provide special implementations for classes that have slightly different semantics or to optimize performance, users can override the standard version of a class, a particular member of the class, or a function template.

The following example shows how specializations work:


//Array.h 
template <class T> class Array { 
private: 
    T *data; 
    int size; 
public: 
    Array(int s = 100); 
    Array(const Array<T> &); 
    T &operator[](int); 
    ~Array(); 
}; 
 
 
//Array.cxx 
#include <string.h> 
 
template <class T> Array<T>(int s) : size(s) { data = new T[size]; } 
 
template <class T> Array<T>::Array(const Array<T> &v) 
{ 
    size = v.size; 
    data = new T[v.size]; 
    for (int i=0; i < size; i++) 
        data[i] = v.data[i]; 
} 
 
//Create a specialization of this member function for Array<char> 
template <class T> Array<char>::Array(const Array<char> &v) 
{ 
    size = v.size; 
    data = new T[v.size]; 
    strcpy(data, v.data); 
} 
 
template <class T> T &Array<T>::operator[](int n) { return data[n]; } 
 
template <class T> Array<T>::~Array() { delete data; } 
 
 
//main.cxx 
#include "Array.h" 
 
int main() 
{ 
    Array<char> v(10); 
 
    for (int i=0; i < 10; i++); 
        v[i] = 'A'; 
 
    Array<char> copy_of_v(v); 
 
    /* ... */ 
 
    return 0; 
} 

To compile and link this example, issue the following commands:


cxx main.cxx 
cxxlink main.obj 

In this example, the member function Array(Array<T> &v) of class template Array is specialized for char by the definition in Array.cxx . When this member function is instantiated, the compiler uses the specialization in Array.cxx instead of using the definition specified by the generic template.

Alternatively, you can supply the specialization in a separate source file as suggested in Section 3.4 (and remove the specialization from Array.cxx ). For example:


 
// Array_char.cxx 
#include "Array.h" 
#include <string.h> 
 
template <class T> Array<char>::Array(const Array<char> &v) 
{ 
    size = v.size; 
    data = new T[v.size]; 
    strcpy(data, v.data); 
} 
 

To compile this source file, use the following command:


cxx Array_char.cxx 

Then you can place the resulting object file in a library to be later linked into your application. Note that, if this source file requests any template instantiations, you will need to partially link the source using cxxlink/noexec and also include the resulting repository object files in your library.

You can declare a specialization in a template declaration file. However, do not define a specialization in a template declaration file because it will cause multiple definitions when the file is included by multiple modules.

5.3.3 Debugging Instantiations

When you specify the /debug qualifier to the cxx command, Compaq C++ instantiates any requested templates with this option as well. If the application's binary file has moved with regard to the repository used to build it, and a relative path name was specified to the /repository qualifier, you must specify a full repository pathname to ensure that the debugger can find the source files.

5.3.4 Linking Applications That Do Not Use Templates

Whenever a default repository exists or you specify a repository, CXXLINK performs the prelink steps associated with automatic template instantiation. To avoid automatic template instantiation, specify the /notemplate_prelink option to the cxxlink command.

5.4 Manual Instantiation

Compaq C++ provides a mechanism for manual instantiation, using the #pragma define_template directive. This directive lets you indicate to the compiler the class or function template to instantiate in conjunction with the actual arguments that the template is to be instantiated with. 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 these sources, enter the following on the command line:


cxx 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 pragma occurs lexically before a function, member function, or static data member template definition, the compiler will be unable to instantiate the corresponding template because the body of that template is not present prior to 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 function 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 
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> > 
 

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 entity<long> and entity< entity<int> > , appending the following pragmas to entity.cxx causes the compiler to generate instantiations of operator < for those requests of entity :


#pragma define_template entity<long> 
 
#pragma define_template entity< entity<int> > 

5.4.1 Using the /template_define Command-Line Option

Alternatively, you could use the /template_define=(all,local,used) command-line qualifer to instantiate templates. Using these qualifer options requires the same template definitions and compilation procedures as previously described for the define_template pragma. For a description of the /template_define command-line qualifer, see the online help on the cxx command.

Considering the examples previously presented 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 with the /template_define command-line qualifer:


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

In this example, the function_never_used function has internal linkage and is not used; therefore, the compiler discards its definition.

5.4.2 Similarities and Differences Between Instantiation Options

Similarities and differences between the three compile-time instantiation options ( all , local , and used ) are as follows:

Determining When to Use the Compile-Time Instantiation Options

The compile-time instantiation options are primarily useful for reducing the time required to build an application.

One approach to reduce build times would be to identify within your application the one module that contains the most uses of template entities. Once identified, this module can be compiled with either the all or used option and the remaining modules compiled with automatic instantiation. This approach avoids the problem of multiply defined symbols at link time that can occur when using these options; this approach also offers the convenience of automatic instantiation.

You can also use the local option to improve build times, provided that the definition of static data members does not have side effects. You can specify the local option for all modules in your application, something you cannot do with the all and used options. With the local option, instantiations are local to each module. These local instantiations are not shared across the various modules within an application. For some applications, multiple copies of the same instance can greatly increase the size of executables.

5.4.3 Rules for Manually Instantiating Templates

For a function template, instantiating the template means interpreting the body of the function template using a specific set of template arguments.

For a class template, instantiating the template means making the following interpretations using a specific set of template arguments:

Consider the following example:


template <class A, class B> class tag { 
    void foo(void); 
    void bar(void); 
} 
 
template <class A, class C> void tag<A,C>::foo(void) {} 
 
#pragma define_template tag<int, int> 
 
template <class A, class B> 
void tag<A, B>::bar(void) {} 

When compiling this code, Compaq C++ does not define tag<int, int>::foo because tag and tag::foo have different template parameters. The compiler does not instantiate tag<int, int>::bar because tag::bar is defined after tag<int, int> is instantiated.

Internal Linkage

Compaq C++ automatically instantiates template functions from templates that define functions with internal linkage. When the following conditions are met, no further user action is required to instantiate template functions:

In the following example, the compiler takes care of instantiating all required instances of sort and entity::operator < :


template <class T> static void sort (T* Array) 
{ 
    /* body of sort */ 
} 
 
template <class T> class entity { 
public: 
    T member; 
    int operator < (const entity<T> &operand) const 
     { return member < operand.member; } 
};     

Defining template functions inline is practical only for very small functions. Compaq C++ replicates an inline function in each compilation unit that uses it, so defining large functions inline can substantially increase the size of a program's object code.


Previous Next Contents Index
  

1.800.AT.COMPAQ

privacy and legal statement