The following sections overview template processing
and describe the instantiation coding methods available to you.
Refer to the technical document Using Templates in HP aC++
for more detailed explanation.
Comparing Template Instantiation Mechanisms
You have the choice of two template instantiation mechanisms.
The default compile-time
mechanism instantiates every template used in a given translation unit in that
translation unit.
You can invoke the automatic instantiation
mechanism by using a comand-line option.
The assigner then decides in which object file an instantiation is placed.
When you link an application or library with aCC, you can combine object
files that have been compiled using either mechanism.
Invoking Instantiation
Scope and Precedence
Explicit instantiation provides instantiation for a particular template
class or template function.
While command line options and the default compile-time
instantiation provide instantiation at the level of the
translation unit.
If you use explicit instantiation in addition to command-line options or
default instantiation, explicit instantiation takes precedence.
For example, using the +inst_all option
requests instantiation of all used template functions and all static
data members and member functions of instantiated template classes within a
translation unit. Whereas, using explicit instantiation
requests instantiation of all members of a particular template class
or a particular template function.
Migration Considerations
See Also:
Why Use Automatic Instantiation
To close a set of link units,
you must use automatic instantiation.
More specifically, you may want to use automatic instantiation for the
following reasons.
- If you provide archive or shared libraries for distribution,
you may want to use the +inst_auto and +inst_close options to insure
consistent behavior between each distribution of your libraries.
- If you provide either archive or shared library products, and your
customers need to use the prior template instantiation default in their
builds, you must build your libraries by using the
+inst_auto and
+inst_close options.
Why Use Compile-Time Instantiation
- Compile-time instantiation is the default. It is easy to use.
- Your code may compile faster when using compile-time instantiation.
- If your development environment uses a version control system that is
sensitive to file modifications, you may want to use the current default,
compile-time instantiation, to avoid major code rebuilds.
Migration Considerations
If you used automatic instantiation with HP aC++ A.02.00 or A.01.04 and prior versions and you wish to continue
using it with subsequent versions of HP aC++, be aware of some
possible migration problems and solutions.
HP aC++, provides two template instantiation mechanisms,
compile-time instantiation (the default)
and
automatic instantiation (invoked by using
a command-line option).
Following are overviews of each type of template processing. For more
detailed information, refer to the technical document
Using Templates in HP aC++.
The major difference between compile-time and automatic instantiation
processing is that, with compile-time instantiation,
the compiler instantiates every template entity it sees in
a translation unit provided it has the required template definition; with
automatic instantiation, the compiler instantiates only what the
assigner tells it to
(except for explicit instantiations). It is the assigner's responsibility
to make sure that every template entity is instantiated and that it is
instantiated only once.
Compile-time Template Processing
- The assigner is not invoked. The compiler places an instantiation
in every .o file in which a template is used and its definition is known.
The linker arbitrarily chooses a .o file to satisfy an instantiation
request (use). Only the chosen instantiation appears in the a.out
or .sl file. Any redundant instantiations in other .o files are ignored.
- No instantiation information is placed in object (.o) files. The
linker is responsible for ignoring duplicate instantiations.
- No .I files are created since the assigner is not used. All .o files
are compiled only once.
Automatic Template Processing
Automatic instantiation uses the assigner, an executable file that runs
at pre-link time to help perform the following tasks:
- The assigner's automatic instantiation algorithm
determines in which object (.o) file an instantiation is placed.
- Instantiation information is placed in object (.o) files. This aids the
assigner in selecting a unique instantiation site for a given instantiation.
- Assignment information resides in a .I file. This indicates to the
compiler which instantiations are to be placed in the corresponding
.o file (upon recompilation).
For More Information
Automatic instantiation involves the creation of .o and .I instantiation files,
described below. By default,
these files are placed in the directory in which you are compiling.
.o Instantiation Files
A .o file contains information generated by the compiler to tell the
assigner about templates in a given translation unit.
It contains the following types of information recognized or produced by
the assigner:
- template members in the translation unit (def)
- demands for instantiation as a result of explicit instantiation (dem)
- requests for instantiation as a result of template object declarations (req)
- actual instantiations resulting from an assignment request (ins)
- assignments of instantiations to this translation unit (asi)
.I Instantiation Files
The .I file, produced by the assigner, contains assignment of instantiations
to a translation unit by the instantiation algorithm. It is an ASCII file.
The command line below uses c++filt to view the instantiation
information in file a.I.
/opt/aCC/bin/c++filt < a.I
The output shown below tells you the a.c translation unit has been assigned
three instantiations.
asi Stack::Stack()
asi Stack::~Stack()
asi Stack::push(int)
If you used automatic instantiation with HP aC++ A.02.00 or A.01.04 and prior versions and you wish to continue
using it with subsequent versions of HP aC++, modify each of your existing aCC command-lines
by adding the +inst_auto option.
This applies to command-lines for:
- creating object files
- creating an executable
- closing a set of object files prior to creating a library (.a or .sl)
- creating a shared library (.sl) provided you do not specify
+inst_none
The following sections describe specific migration scenarios and
illustrate possible migration problems and solutions.
An existing compiler defect may be more apparent, if in HP aC++ A.02.00 or A.01.04 and prior versions
you built a shared library using automatic instantiation
(the prior default using the assigner) and
now build that library using the current default (compile-time) instantiation.
The defect relates to template objects with constructors or other runtime
initializers that have been globally defined in more than one shared library
on the link line. If such an object is defined in n shared libraries,
it will be initialized and destructed n times at runtime.
When building the same application with the current default, the libraries
are not closed prior to the final link, and the likelihood of a template
symbol being defined in more than one shared library will increase.
If in HP aC++ A.02.00 or A.01.04 and prior versions you built an archive library using automatic instantiation
(the prior default using the assigner) and you rebuild
that library using the current default (compile-time) instantiation,
it is possible that duplicate
symbol problems not apparent in the prior release will generate errors in the
current release.
This is because the current default uses the linker rather than the assigner
to determine which object file to pick to satisfy instantiation requests.
For example, when your archive library is linked with an application, library
objects in the link may be different than those used when linking the library
in a prior release.
Following are two examples of building an archive library, one built with
+inst_auto/+inst_close (the prior default), the other built with the current
(compile-time) default.
Building an Archive Library with +inst_auto/+inst_close
Suppose for lib.inst_auto.a, the linker chooses foo2.o to
resolve symbol x, and foo3.o to resolve symbol stack <int>.
Symbols x, y, and stack <int> are each resolved with no duplicates.
lib.inst_auto.a
-------------------------------------------------
| foo.o | foo2.o | foo3.o |
| | | stack |
| x | x | y |
| y | | |
-------------------------------------------------
Building an Archive Library with the Default (Compile-time Instantiation)
Suppose for lib.default.a, the linker chooses foo2.o to
resolve symbol x, and foo.o to resolve symbol stack <int>.
Symbols x, y, and stack <int> are each resolved, but now there's
a duplicate definition of symbol x. This will cause a linker
duplicate symbol error. This is really a user error, but was
not visible before.
NOTE:
Note that this example is not meant to account for all cases
of changed behavior.
lib.default.a
-------------------------------------------------
| foo.o | foo2.o | foo3.o |
| stack | stack | stack |
| x | x | y |
| y | | |
-------------------------------------------------
What happens when you mix .o and .a files compiled with HP aC++ A.02.00 or A.01.04 and prior versions with
files compiled with subsequent versions of HP aC++? The linker
gives an old symbol precedence over a new symbol of the same name. For
example:
foo1.o (old) foo2.o (new) foo3.o (new)
----------- ----------- -----------
func func func
[old symbol] [new symbol] [new symbol]
In this case the linker chooses the func <int> from foo1.o and ignores
the other two, because the old func <int> takes precedence over any
of the new ones. Note that if there were more than one old func <int>,
the linker would give a duplicate symbol error.
A Special Case of Mixing Old .o and .a Files with New Ones
bar.a (old)
-------------------------- ------------------------------------
| foo1.o (new) | | bar1.o | bar2.o | |
|------------------------| |--------------|--------| ....... |
| func [new symbol] | | func | | |
-------------------------- | [old symbol] | | |
------------------------------------
Since old symbols take precedence, you would expect the linker
to choose func <int> from bar1.o. However, since bar1.o is part
of an archive library, the linker will never even try to load it
unless it's needed to resolve some other symbol in foo1.o.
So if ld loads bar1.o, then it will choose func <int> from bar1.o
However, if ld does not load bar1.o, it will choose the only
func <int> that's available, and that's the one in foo1.o.
And bar1.o might not be loaded, even though it was loaded under
the old default. This behavior may not cause a problem, however,
in some cases it may. For example, in a correctly written program
in which multiple definitions of func <int> are equivalent, there
is no problem. However, if multiple definitions of func <int> are
not equivalent, the compiler does not detect the error.
Linker Error Checking Messages
Given all of the above, the linker provides error checking to help
find out what may be happening.
- By default, ld issues generic warnings like the following when it
sees a new/old pair: aCC old.o new.o
/opt/aCC/lbin/ld: (Warning) Linker features were used that may not
be supported in future releases. The +vallcompatwarnings option
can be used to display more details, and the ld(1) man page contains
additional information. This warning can be suppressed with the
+vnocompatwarnings option.
- If you want more information: aCC old.o new.o -Wl,+vallcompatwarnings
/opt/aCC/lbin/ld: (Warning) An automatic template instantiation for
member "func ()" in file t.o has been overridden by an explicit
definition in fi le new.o. This behavior may not be supported in
future releases.
- If you want to see duplicate symbol messages based on what would
happen if compatibility with old .a and .o files were not
supported, issue the command-line: aCC old.o new.o -Wl,+strictctti
This generates a message like the following:
/opt/aCC/lbin/ld: Duplicate symbol "func()" in files old.o and
new.o /opt/aCC/lbin/ld: Found 1 duplicate symbol(s)
- To suppress all linker compatibility warnings, use the command-line:
aCC old.o new.o -Wl,+vnocompatwarnings
You request explicit instantiation by using the explicit template instantiation
syntax (as defined in the ANSI/ISO C++ International Standard) in your source file.
You can request explicit instantiation of a particular template class
or a particular template function.
In addition, member functions and static data members of class templates
may be explicitly instantiated.
Explicit instantiation of a class instantiates all member functions
and static data members of that class, regardless of whether or not
they are used.
For example, following is a request to explicitly instantiate the
Table template class with char*:
template class Table;
When you specify an explicit instantiation, you are asking the compiler to
instantiate a template at the point of the explicit instantiation in the
translation unit in which it occurs.
Usage
This might, for example, be useful when you are building a library for
distribution and want to create a set of compiler-generated template
specializations that you know will most commonly be used.
Then when an application is linked with
this library, any of these commonly used specializations need not
be instantiated.
Another scenario might be a frequently used library that contains a repository
of template specializations for your development team.
Instantiating all such specializations in one, known translation unit
would allow easy maintenence when changes are needed and eliminate
cases of duplicate definition.
Performance
Although time is required to analyze and design code for explicit instantiation,
compilation may be faster than for the equivalent
implicit instantiation.
Class Template
Following are examples of explicit and implicit instantiation syntax for a
class template:
template class Array; // forward declaration for the
// Array class template
template class Array {/*...*/}; // definition of the
// Array class template
template class Array ; // request to explicitly
// instantiate Array
// template class
Array tc; // use of Array
// template class which
// results in implicit
// instantiation
Function Template
Following are examples of explicit and implicit instantiation syntax for a
function template:
template void sort(Array &); // declaration for the sort()
// function template
template void sort(Array &v) {/* ... */};
// definition of the sort()
// function template
template void sort (Array &); // request to explicitly
// instantiate the sort ()
// template function
//NOTE is not requird if the compiler can deduce this.
void foo() {
Array ai;
sort(ai); // use of the sort ()
} // template function which
// results in implicit instantiation
For More Information
- A version of the Final Draft International Standard
is publically available on the World Wide Web. Refer to the draft for additional
details including explicit specialization syntax.
All template options on an aCC command-line
apply to every file on the command line.
If you specify more than one option on a command-line, only the last option
takes effect.
By default, compile-time instantiation is in effect. Instantiation is
attempted for any use of a template in the translation unit where the
instantiation is used. All used template functions,
all static data members and member functions of instantiated template classes,
and all explicit instantiations are instantiated
in the resulting object file.
If there are duplicate instantiations at link-time, the linker arbitrarily
selects an instantiation for inclusion in the a.out or shared library.
The following command-lines are equivalent; each compiles a.C using
compile-time instantiation.
aCC -c +inst_compiletime a.C
aCC -c a.C
Scope
If your source code contains templates and you do not specify any template
command-line options
nor explicit instantiations,
compile-time instantiation takes place for any use of a template.
If you specify a template command-line option, the option takes precedence
for all translation units on the command line.
Any explicit instantiation takes precedence over either a command-line
option or compile-time instantiation.
Usage
Compared with developer-directed instantiation, compile-time instantiation
involves less coding time for the developer. However,
the design of your application may require the use of some form of
directed instantiation.
The HP WDB Debugger and the HP/DDE Debugger support C++ templates.
For More Information
You can create class templates and function templates.
A template defines a group of classes or functions. A template can have one or
more types as parameters. When you use a template, you provide the
particular types
or constant expressions as actual parameters thereby creating a
particular object or function.
A class template defines a family of classes.
To declare a class template, you use the keyword template followed by the
template's formal parameters. Class templates can take parameters that
are either types or expressions. You define a template class in terms of those
parameters. For example, the following is a class template for a simple stack
class. The template has two parameters, the type specifier T and the
int parameter size. The keyword class in the < > brackets
is required to declare any template type parameters.
The first parameter T is used for the stack element type. The second
parameter is used for the maximum size of the stack.
template<class T, int size>
class Stack
{
public:
Stack(){top=-1;}
void push(const T& item){thestack[++top]=item;}
T& pop(){return thestack[top--];}
private:
T thestack[size];
int top;
};
Class template member functions and member data use the formal parameter type,
T, and the formal parameter expression, size.
When you declare an instance of the class Stack, you provide an actual
type and a constant expression. The object created uses that type and value
in place of T and size, respectively. For example, the
following program uses the Stack class template to create a stack of
20 integers by providing the type int and the value 20 in the object
declaration:
void main()
{ Stack<int,20> myintstack;
int i;
myintstack.push(5);
myintstack.push(56);
myintstack.push(980);
myintstack.push(1234);
i = myintstack.pop();
}
The compiler automatically substitutes the parameters you specified, in this
case int and 20, in place of the template formal parameters. You can
create other instances of this template using other built-in types as well as
user-defined types.
A function template defines a family of functions.
To declare a function template, use the keyword template to define
the formal parameters, which are types, then define the function in terms
of those types. For example, the following is a function template for a swap
function. It simply swaps the values of its two arguments:
template
void swap(T& val1, T& val2)
{
T temp=val1;
val1=val2;
val2=temp;
}
The argument types to the function template swap are not specified.
Instead, the formal parameter, T, is a placeholder for the types.
To use the function template to create an actual function instance (a template
function), you simply call the function defined by the template and provide
actual parameters. A version of the function with those parameter types is
created (instantiated).
For example, the following main program calls the function swap twice,
passing int parameters in the first case and float parameters in the
second case. The compiler uses the swap template to automatically create
two versions, or instances, of swap, one that takes int parameters and
one that takes float parameters.
void main()
{ int i=2, j=9;
swap(i,j);
float f=2.2, g=9.9;
swap(f,g);
}
Other versions of swap can be created with other types to exchange the
values of the given type.