United States |
Previous | Contents | Index |
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.
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. You can use a name mapping file to direct the compiler to the location of the template definition file. To get started quickly using automatic instantiation, see laz Section 5.1. For details, see Section 5.2.
Compaq C++ provides the following methods to manually instantiate templates:
By default, 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> v; v[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 a header file. 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 extension. 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> v; v[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 Compaq C++ performs automatic template instantiation by default, you do not need to specify the -pt option on the command line. To build the main program, use a cxx command such as the following:
cxx -I./include main.cxx |
This command assumes that main.cxx is located in the current directory and Array.h and Array.cxx are in the ./include subdirectory.
Compaq C++ creates the default repository in ./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 '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:
// Array__T6String.cxx #include "Array.h" //template declaration file #include <String.h> //argument declaration file #include "Array.cxx" //template definition file typedef Array<String> __dummy_; |
Once compilation of main.cxx completes, Compaq C++ invokes a prelinker to determine the instantiation requests generated by the application. For each request, the prelinker 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 Compaq C++ links the application.
The following are some other helpful notes to get started quickly:
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:
The following cxx command-line options are specific to automatic instantiation of templates:
-nopragma_template | Directs the compiler to ignore any #pragma define_template directives. This option is provided for users who want to quickly migrate to automatic instantiation without having to remove all the pragma directives from their code base. |
-nopt | Directs the compiler to not automatically instantiate templates. |
-pt | Directs the compiler to automatically instantiate templates. Each function template is placed in its own instantiation file, and member functions and static data members of each class template are grouped into one instantiation file. This option is the default. |
-ptr pathname |
Specifies a repository, with
./cxx_repository
as the default. If you specify several repositories, only the first is
writable, and the rest are read only.
Specifying this option at link time enables Compaq C++ to recognize and use the various template instantiations within the specified repository. If you use this option, make sure that the repository specified at compile time is the same one specified at link time. |
-pts | Directs Compaq C++ to automatically instantiate needed templates. Each member function or static data member is instantiated in its own separate file. |
-ptsuf" list" | Specifies a list of file name suffixes that are valid for template definition files. Items in the list must be separated by commas and each suffix preceded by a period. A suffix may have no more than eight characters excluding the beginning period. The default is ".cxx,.CXX,.C,.cc,.CC,.cpp,.c" . |
-ptv | Turns on verbose or verify mode to display each phase of instantiation as it occurs. This option is useful as a debugging aid; see Section 5.2.9.1.3. |
-use_ld_input |
Directs Compaq C++ to record the names of requested automatic
template instantiation object files in a linker options file in the
writable repository.
Compaq C++ then adds the linker options file to the ld command line using the -input file linker command line option. The default behavior is to add the instantiation file names directly to the ld command line. You can use the -use_ld_input option to reduce long ld command lines that cause linking to fail because of "Arg list too long" errors. |
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 -ptr command-line option. 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 UNIX 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.cmd | Dependency file containing the command-line options |
Vector__Ti.cxx | Instantiation source file |
Vector__Ti.o | Instantiation object file |
Vector__Ti.o.d | Dependency file containing the list of header files |
Vector__Ti.req | Instantiation request file |
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 header 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.map . 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:
name
A simple name to identify the template to the compiler. Qualified names are unnecessary and not allowed. Parentheses, return types, and parameter lists are also not allowed.filename
The path name of the file containing the template definition. Each file listed will be included in the resulting instantiation source file.
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.
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 .
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 |
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"; |
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):
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_; |
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:
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:
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 the use of the
-define_templates
option or explicit instantiation or
#pragma define_template
statements within the sources.
When you use automatic template instantiation, the basic method is the same: you want to compile all the library sources and place the resulting object files into an object library. However you first need to examine your sources and determine whether or not you need the explicit instantiation statements. Also you need to change your build procedure to specify different options on the cxx command line. See the following paragraphs for details.
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:
As you compile each library source, you must include the following options on your cxx command line:
Do not specify the -define_templates option on your cxx command.
If you intend to create a shareable library from these sources and you plan to let your customers preempt the symbols in your shareable library, you must also specify the -preempt_symbol option on your cxx command line.
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.sh , would be as follows:
lroot=pathname/windows_library lbuild=$lroot/build # Compile each source separately using -Hf # # -Hf option ensures that both the source and template instantiations # are compiled; do not use the -c option because that prevents # compilation of the template instantiations # # -ptr 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 command # # -o option causes the source object files to be placed in the build # directory cxx $lroot/src/w1.cxx -I $lroot/include -ptr $lbuild/repository \ -o $lbuild/w1.o -Hf cxx $lroot/src/w2.cxx -I $lroot/include -ptr $lbuild/repository \ -o $lbuild/w2.o -Hf # ... and so on for all library source files # And now place all the source objects and template instantiation objects # into the object library ar r $lbuild/libwindows.a $lbuild/*.o $lbuild/repository/*.o |
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 solely used 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 as
described 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
-all
option on your
ld
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.
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, Compaq C++
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, Compaq
recommends that you 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
cxx
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.
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
-define_templates
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.
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
Because you will be performing a final link of your sources, you can
delay the prelink phase until the final link. Therefore, you can
continue to compile your application sources using the
-c
option and do not need to specify the
-Hf
option as described in Section 5.2.9.1. However you must include the
following options on your
cxx
commands:
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:
5.2.9.1.3 Using the -ptv Option
When you specify the
-ptv
option during compilation, Compaq C++ displays informational
messages about the repository or repositories being used. When you
specify the
-ptv
option during the prelink or link phase, Compaq C++ displays
informational messages about the repositories as well as each
unresolved symbol, each instantiation object file found, and each
compilation of an instantiation source file.
5.2.9.2 Building an Application
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.
Because you will be delaying the compilation of template
instantiations until the final link, you must specify
full pathname
for all include directories listed in your
cxx
command. Otherwise the compilation of template instantiations may fail
due to missing include files.
You must specify the same repository for each source file across
all components, and on your final link command as well.
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 -ptr option. The ui build procedure, build_ui.sh , would look like the following, and the other component procedures would be similar:
sroot=pathname/spreadsheet_proj uiroot=$sroot/ui uibuild=$uiroot/build fibuild=$sroot/final_build # Compile each source separately using -c # # -ptr 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 # # -I option specifies the full pathname so that later compilation of # the template instantiations will succeed # # -o option causes the source object files to be placed in the ui/build # directory cxx $uiroot/src/ui1.cxx -I$uiroot/include -ptr $fibuild/repository \ -o $uibuild/ui.o -c # ... and so on for each source in this component |
Your final build procedure, build_spreadsheet.sh , would look like the following:
sroot=pathname/spreadsheet_proj uiroot=$sroot/ui uibuild=$uiroot/build comproot=$sroot/comp compbuild=$comproot/build rptroot=$sroot/rpt rptbuild=$rptroot/build fibuild=$sroot/final_build # Compile the main program and create the final executable # # -ptr 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 $uibuild/*.o $rptbuild/*.o $compbuild/*.o \ -I$uiroot/include -I$comproot/include -I$rptroot/include \ -ptr $fibuild/repository -o $fibuild/spreadsheet |
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.a , the following changes are necessary:
cxx $uiroot/src/ui1.cxx -I$uiroot/include -I$library/include \ -ptr $fibuild/repository -o $uibuild/ui.o -c |
cxx $sroot/main/main.cxx $uibuild/*.o $rptbuild/*.o $compbuild/*.o \ -I$uiroot/include -I$comproot/include -I$rptroot/include \ -I$library/include -L$library/build -lwindows \ -ptr $fibuild/repository -o $fibuild/spreadsheet |
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 this file using the same command as described in Section 5.2.9.1.1:
cxx -Hf -ptr ./lib_repository vector_inst.cxx |
In this example, the cxx command will compile vector_inst.cxx and create and compile any further instantiations that are needed. The object file vector_inst.o will be in the current directory. If additional instantiations are generated, their object files will be in the repository ./lib_repository .
If you have multiple source files similar to vector_inst.cxx , compile each of them using the same -Hf option and the same repository.
If you intend to release a shared library of instantiations and let customers override any instantiations in your library, you must compile your instantiation files with the -preempt_symbol option. |
You can then use the following commands to create an object library called libinst.a :
ar r libinst.a vector_inst.o ./lib_repository/*.o ranlib libinst.a |
For the C++ Standard Library, Compaq provides a shell script called
/usr/lib/cmplrs/cxx/build_common_instantiation_library.sh
that automatically creates a library of common instantiations
for the STL containers and other class templates. You can examine this
script as an example of makefile and source files.
5.2.10 Useful Conventions
This section describes ways to structure and name an application's
files to make the best use of automated template instantiation.
5.2.10.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 also can 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.2.10.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 command:
% cxx main.cxx |
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 -c Array_char.cxx |
Then you can place the resulting object file in a library to be linked later into your application. Note that, if this source file requests any template instantiations, you will need to specify the -Hf option on your cxx command 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.2.10.3 Debugging Instantiations
When you specify one of the
-g
options 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
-ptr
command-line option, you must specify a full repository path name to
ensure that the debugger can find the source files.
5.2.10.4 Linking Applications That Do Not Use Templates
By default, Compaq C++ performs the prelink steps associated with
automatic template instantiation. To avoid automatic template
instantiation, specify the
-nopt
option to the
cxx
command.
Compaq C++ provides a mechanism for manual instantiation, using the
#pragma define_template
directive. This directive lets you tell the compiler what class or
function template to instantiate in conjunction with the actual
arguments that the template is to be instantiated with.
The
#pragma define_template
directive has the following format:
5.3 Manual Instantiation
#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 functions templates into header files, and instantiate all instances of a particular template from a single compilation unit. The following example shows how to do this:
// sort.h template <class T> void sort (T*); // entity.h template <class T> class entity { public: T member; int operator < (const entity<T> &) const; }; // main.cxx #include "sort.h" #include "entity.h" int al[100]; float a2[100]; entity<int> a3[100]; int main() { sort(a1); sort(a2); sort(a3); return 0; } // sort.cxx #include "sort.h" #include "entity.h" template <class T> void sort (T* array) { /*body of sort*/ } #pragma define_template sort<int> #pragma define_template sort<float> #pragma define_template sort<entity<int> > |
Compiling the following file provides a definition of entity::operator < with type int :
// entity.cxx #include "entity.h" template <class T> int entity<T>::operator < (const entity<T> &operand) const { return member < operand.member; } #pragma define_template entity<int> |
To compile this example, issue the following command:
cxx main.cxx sort.cxx entity.cxx |
If the program uses other instantiations of entity in other compilation units, you can provide definitions of operator < for those entities by adding define_template pragmas to entity.cxx . For example, if other compilation units use 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> > |
Alternatively, you could use the -define_templates command-line option to instantiate templates. Using the -define_templates option requires the same template definition and compilation procedures as previously described for the define_template pragma. For a description of the -define_templates option on the cxx command, see the cxx (1) reference page.
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:
// 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); } |
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.
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 |