Skip to content. Skip to navigation

ICTP Portal

Sections
You are here: Home Manuals on-line PGI Compiler pgiws_ug PGI Workstation User's Guide - 12 C++ Template Instantiation
Personal tools
Document Actions

PGI Workstation User's Guide - 12 C++ Template Instantiation

<< << " border=0> >> > " border=0> Title Contents Index Home Help

12 C++ Template Instantiation


A template defines a family of types or functions. For example, the following code fragment shows a template declaration of a class vect. This template declaration can be used to declare vector objects. By supplying different types for the parameter T, different template class definitions will be instantiated or generated.

// define a template 
template<class T> class vect{
private:
T * v;
typedef int vect_index_t;
vect_index_t size;
public:
vect(vect_index_t x) { size = x; v = new int[x];};
T& operator[](vect_index_t);
T& element(vect_index_t i);
}; template<class T> T& vect<T>::element(vect_index_t i)
{
return v[i];
}

This program fragment shows the template class vect being used.

// use the template
vect<int> x(80);
vect<double> d(20); void foo(void)
{
int j = x.element(5);
double f = d.element(6);
}

The previous program requires two instantiations of template class vect: one where T is int and one where T is double. It would seem the compiler could just generate these instantiations, but unfortunately things are not that simple.

If the template declaration of class vect was in an include file, and another module included it and used it, other instantiations of this template might be needed. Another module might also require an instantiation where T is int. In this case, we would like only one instantiation of template class vect where T is int.

C++ also allows specialization of a template entity. This is a type specific version to be used in place of the version that would have been generated from the template. In the above example, someone could write a specialization for type int:

int& vect<int>::element(vect_index_t i) 
{
// check bounds for int vectors
if (i >= size){
extern void error(char *);
error("vect index out of bounds\n");
}
return v[i];
}

In this case, we would like to use the specialized member function element.

C++ also dictates that unreferenced template functions should not be compiled.

So, for all of these reasons, the compiler cannot know what instantiations are required or in which modules to put them until the whole program is linked. The programmer should have an idea where these templates should be expanded. First we discuss two methods the programmer can use to tell the compiler where to put template instantiations. Then we discuss an automatic instantiation scheme.

12.1 Command Line control of template instantiation

Normally, when a file is compiled, no template entities are instantiated (except those assigned to the file by automatic instantiation [see below]). The overall instantiation mode can, however, be changed by a command-line option:

-Wc,-tnone
Do not automatically create instantiations of any template entities. This is the default. It is also the usually appropriate mode when automatic instantiation is done.
-Wc,-tused
Instantiate those template entities that were used in the compilation. This will include all static data members for which there are template definitions.
-Wc,-tall
Instantiate all template entities declared or referenced in the compilation unit. For each fully instantiated template class, all of its member functions and static data members will be instantiated whether or not they were used. Nonmember template functions will be instantiated even if the only reference was a declaration.
-Wc,-tlocal
Similar to -tused except that the functions are given internal linkage. This is intended to provide a very simple mechanism for those getting started with templates. The compiler will instantiate the functions that are used in each compilation unit as local functions, and the program will link and run correctly (barring problems due to multiple copies of local static variables.) However, one may end up with many copies of the instantiated functions. -tlocal cannot be used in conjunction with automatic template instantiation.

12.2 Pragma control of template instantiation

Instantiation pragmas control the instantiation of specific template entities or sets of template entities. There are three instantiation pragmas:

  • The instantiate pragma causes a specified entity to be instantiated.
  • 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 will be supplied.
  • The can_instantiate pragma indicates that a specified entity can be instantiated in the current compilation, but need not be; it is used in conjunction with automatic instantiation, to indicate potential sites for instantiation if the template entity turns out to be required.

The argument to the instantiation pragma may be:

  • A template class name A<int>
  • A member function name A<int>::f
  • A static data member name A<int>::i
  • A member function declaration void A<int>::f(int, char)
  • A template function declaration char* f(int, float)

A pragma directive in which the argument is a template class name (for example 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 pragma do_not_instantiate. For example,

#pragma instantiate A<int>
#pragma do_not_instantiate A<int>::f

The template definition of a template entity must be present in the compilation for an instantiation to occur. If an instantiation is explicitly requested by use of the instantiate pragma and no template definition is available or a specific definition is provided, an error is issued.

template <class T> void f1(T); // No body provided 
template <class T> void g1(T); // No body provided
void f1(int) {} // Specific definition
void main()
{
int i;
double d;
f1(i);
f1(d);
g1(i);
g1(d);
}
// error - specific definition #pragma instantiate void f1(int)
// error - no body provided#pragma instantiate void g1(int)

f1(double) and g1(double) will not be instantiated (because no bodies were supplied) but no errors will be produced during the compilation (if no bodies are supplied at link time, a linker error will be produced).

A member function name (e.g., A<int>::f) can only be used as a pragma argument if it refers to a single user defined member function (i.e., 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, as in

#pragma instantiate char* A<int>::f(int, char*)

The argument to an instantiation pragma may not be a compiler generated function, an inline function, or a pure virtual function.

12.3 Automatic template instantiation

The goal of an automatic instantiation mode is to provide painless instantiation. The programmer should be able to compile source files to object code, then link them and run the resulting program, and never have to worry about how the necessary instantiations get done. In practice, this is hard for a compiler to do.

Our approach requires that for each instantiation required, there is some (normal, top-level, explicitly-compiled) source file that contains both the definition of the template entity and of any types required for the particular instantiation.

Isn't this always the case? No. Suppose that file A contains a definition of class X and a reference to Stack<X>::push, and that file B contains the definition for the member function push. There would be no file containing both the definition of push and the definition of X.

This requirement can be met in various ways:

  1. Each .h file that declares a template entity also contains either the definition of the entity or includes another file containing the definition.
  2. Implicit inclusion: when the compiler sees a template declaration in a .h file and discovers a need to instantiate that entity, it is given permission to go off looking for an associated definition file having the same base name and a different suffix, and it implicitly includes that file at the end of the compilation. This method allows most programs written using the cfront convention to be compiled. See implicit inclusion section.
  3. The ad hoc approach: the programmer makes sure that the files that define template entities also have the definitions of all the available types, and adds code or pragmas in those files to request instantiation of the entities there.

The automatic instantiation method works as follows:

  1. The first time the source files of a program are compiled, no template entities are instantiated. However, the generated object files contain information about things that could have been instantiated in each compilation.
  2. When the object files are linked together, a special pre-linker program called pgprelnk is run. It examines the object files, looking for references and definitions of template entities, and for the added information about entities that could be instantiated.
  3. If pgprelnk finds a reference to a template entity for which there is no definition anywhere in the set of object files, it looks for a file that indicates that it could instantiate that template entity. When it finds such a file, it assigns the instantiation to it. The set of instantiations assigned to a given file, say abc.C, is recorded in an associated .ii file, for example, abc.ii.
  4. The pgprelnk then executes the compiler again to recompile each file for which the .ii file was changed.
  5. When the compiler compiles a file, it reads the .ii file for that file and obeys the instantiation requests therein. It produces a new object file containing the requested template entities (and all the other things that were already in the object file).
  6. pgprelnk repeats steps 3 thru 5 until there are no more instantiations to be adjusted.
  7. The object files are linked together.

Once the program has been linked correctly, the .ii files contain a complete set of instantiation assignments. From then on, whenever source files are recompiled, the compiler will consult the .ii files and do the indicated instantiations as it does the normal compilations. That means that, except in cases where the set of required instantiations changes, the pgprelnk step from then on will find that all the necessary instantiations are present in the object files and no instantiation assignment adjustments need be done. That's true even if the entire program is recompiled.

If the programmer provides a specialization of a template entity somewhere in the program, the specialization will be seen as a definition by the pgprelnk step. Since that definition satisfies whatever references there might be to that entity, the pgprelnk program will see no need to request an instantiation of the entity. If the programmer adds a specialization to a program that has previously been compiled, the pgprelnk program will notice that too and remove the assignment of the instantiation from the proper .ii file.

The .ii files should not, in general, require any manual intervention. One exception: if a definition is changed in such a way that some instantiation no longer compiles (it gets errors), and at the same time a specialization is added in another file, and the first file is being recompiled before the specialization file and is getting errors, the .ii file for the file getting the errors must be deleted manually to allow the pgprelnk to regenerate it.

If the pgprelnk changes an instantiation assignment, it will issue a message like

C++ pgprelnk: f__10A__pt__2_iFv assigned to file test.o
C++ pgprelnk: executing: /usr/pgi/bin/pgCC -c test.c

The name in the message is the mangled name of the entity.

The automatic instantiation scheme can coexist with partial explicit control of instantiation by the programmer through the use of pragmas or command-line specification of the instantiation mode.

12.4 Implicit inclusion

When implicit inclusion is enabled, the front-end assumes that if it needs a definition to instantiate a template entity declared in a ".h" file it can implicitly include the corresponding ".C" file to get the source code for the definition. For example, if a template entity ABC::f is declared in file xyz.h, and an instantiation of ABC::f is required in a compilation but no definition of ABC::F appears in the source code processed by the compilation, the compiler will look to see if a file xyz.C exists, and if so it will process it as if it were included at the end of the main source file.

To find the template definition file for a given template entity the front-end needs to know the full path name of the file in which the template was declared and whether the file was included using the system include syntax (e.g., #include <file.h>). This information is not available for preprocessed source containing #line directives. Consequently, the front-end will not attempt implicit inclusion for source code containing #line directives. The set of definition-file suffixes tried is ".c", ".C", ".cpp", ".CPP", ".cxx", ".CXX", ".cc", and ".c++".

Implicit inclusion works well alongside automatic instantiation, but the two are independent. They can be enabled or disabled independently, and implicit inclusion is still useful when automatic instantiation is not done. The implicit inclusion mode can be turned on or off using the -implicit_include and
-no_implicit_include command-line options.

Implicit inclusions are only performed during the normal compilation of a file, (i.e., not when doing only preprocessing). A common means of investigating certain kinds of problems is to produce a preprocessed source file that can be inspected. When using implicit inclusion it is sometimes desirable for the preprocessed source file to include any implicitly included files. This may be done using the -no_preproc_only command-line option. This causes the preprocessed output to be generated as part of a normal compilation. When implicit inclusion is being used, the implicitly included files will appear as part of the preprocessed output in the precise location at which they were included in the compilation.

12.5 Template Libraries

Template libraries contain template definitions. Special care must be taken when building template libraries that reference other template libraries. Since template instantiation occurs at link time, and libraries are archived rather than linked, the user must invoke a special pre-link step to instantiate templates that are directly referenced in the library code. The command line flags --one_instantiation_per_object, --template_dir, and --prelink_objects facilitate this pre-link step.

The option --one_instantiation_per_object is used on each .c file to put each template instantiation in its own object, so that the linker may access it independently. The option --template_dir is used only if the user wants direct control over the template directory name. The option --prelink_objects is used in combination with --one_instantiation_per_object on the list of archivable objects to generate the instantiations. For example, a makefile which looks as follows:

. . .
CC = pgCC
CCFLAGS = -O2
file1.o: file1.cc
$(CC) $(CCLAGS) -c file1.cc

file2.o: file2.cc
$(CC) $(CCLAGS) -c file2.cc
. . .

libX.a: file1.o file2.o . . .
ar cr libX.a file1.o file2.o . . .

might be modified to look as follows:

. . .
CC = pgCC
CCFLAGS = -O2 --one_instantiation_per_object
file1.o: file1.cc
$(CC) $(CCLAGS) -c file1.cc

file2.o: file2.cc
$(CC) $(CCLAGS) -c file2.cc
. . .

libX.a: file1.o file2.o . . .
# This implies --one_instantiation_per_object --prelink_objects
$(CC) ($CCFLAGS) --prelink_objects file1.o file2.o

# Template.dir/*.o contains the templates instantiated above
ar cr libX.a file1.o file2.o Template.dir/*.o . . .


<< << " border=0> >> > " border=0> Title Contents Index Home Help

Powered by Plone This site conforms to the following standards: