| From: DEC:.REO.REOVTX::HUDSON "[email protected] - UK Software
Partner Engineering 830-4121" 10-FEB-1997 14:43:18.13
To: nm%vbormc::"[email protected]"
CC: HUDSON
Subj: RE:POINT No 19446 C++ template instantiation
Hello Wolfgang Haefelinge
Thanks for your question on C++ template instantiation.
By default, the C++ compiler does auto-instantiation (the "-pt" switch). As
you've found, this can be slow if (usually on the first time) the
cxx_repository directory has to be created and populated.
When you use auto-instantiation, then the cxx_repository is used in two ways.
First, the compiler will put "source code" in there for any templates it
encounters. Second, when the linker runs, if it comes across any undefined
symbols, for example something like "myclass<int>::myclass()", then it asks the
C++ compiler to run again and generate the necessary code. This means the c++
compiler looks in cxx_repository for the files needed to build a template
instance of the type needed, compiles them, puts the object code in
cxx_repository, and then gets the linker to run again.
I have seen people say that this can be very slow, and the response from the
engineering team is (1) Yes we know and we're going to make it better, and (2)
bear in mind that after a few iterations, you probably will have the necessary
modules compiled in the cxx_repository directory so that subsequent builds
should be quicker (the compiler only needs to instantiate a "myclass<int>"
once, and then that can be used many times.
But, to answer your question specifically, it is possible to force the compiler
to generate template instances on its first pass. In fact this used to be the
only way to get templates working before auto-instantiation appeared (in 5.3 of
C++ I think). The disadvantage is that you need to know ahead of time what
template instances you need.
Below I include two things. First, a memo I wrote describing how you can do
manual template instantiation. This was written before auto-instantiation was
available.
Second, I include a set of files I've used on Unix to show how the same program
can be built with and without auto-instantation. This is a contrived and very
small case, but it does seem faster when you do manual instantiation.
I hope this is helpful to you
Regards
Nick Hudson
Digital Software Partner Engineering
================================================================================
DEC C++ does not presently have automated template instantiation; it relies on
the programmer providing information to the compiler to indicate which
functions are required. Chapter 4 of the "Using DEC C++" manual describes the
ways in which a programmer can provide information to instruct the compiler to
instantiate particular forms of class or function templates. Briefly, there
are three ways in which template functions are instantiated:
1. Function templates that have internal linkage
Any function that is static or inline has "internal linkage" (i.e. is
local to the module in question) and will be automatically instantiated
by the compiler when it sees a reference to the function.
This is viable when all of the template source code is in the same module as
the instantiations of the functions, so will be practical in a minority of
cases.
For example, in the following case, the compiler will automatically
instantiate "int" and "float" versions of the "add" function:
#include <iostream.h>
template<class T>
inline T add(T x, T y)
{
return (x+y);
}
main()
{
int a,b,c;
float x,y,z;
a = 1; b = 2;
x = 1.2; y = 2.0;
c = add(a,b);
z = add(x,y);
cout << c << " " << z << endl;
}
2. Compiler switch "-define_templates"
For functions that don't have internal linkage, a compiler qualifier can be
used to force instantiation. For example, in the program above, if the
"add" function were not specified as "inline", the compiler could still be
made to instantiate "int" and "float" versions of "add" if the compiler
switch were used.
This is viable when all of the template source code is available at the time
the compiler is parsing the module contain the function instantiations.
3. Pragma "#pragma define_template"
This pragma can be used to tell the compiler that a particular form of the
function or class template needs to be instantiated. For example:
// This is add.cxx
template <class T>
T add(T x, T y)
{
return (x + y);
}
#pragma define_template add<int>
#pragma define_template add<float>
This mechanism allows full control over which versions of the template are
instantiated, but means that the programmer has specifically to use the
pragma rather than rely on the compiler to work out which instantiations are
required.
[Although I've used the term "template function" in the above, template classes
can be instantiated in the same way]
Typically we find that customers are able to use these mechanisms (mostly
#pragma) to build their applications. However, it does mean that templates are
difficult to use, and in many cases the organisation of source modules has to
be changed to accommodate the deficiencies in the compiler.
Work is being done by the C++ engineering team to include automated template
instantiation in a future release of the compiler, which will ease these
problems.
================================================================================
////////////////////////////////////////////////////////////////////////////////
///////////// Makefile ///////////////
prog_auto : prog.cxx pair.h pair.cxx
cxx -o prog_auto -pt prog.cxx
prog_noauto : prog.cxx pair.h pair.cxx
cxx -c -o pair.o -DNOAUTO pair.cxx
cxx -o prog_noauto -nopt -DNOAUTO prog.cxx pair.o
////////////////////////////////////////////////////////////////////////////////
// pair.h
#ifndef __PAIR_H
#define __PAIR_H
template<class T>
class pair {
T item1;
T item2;
public:
pair();
pair(const T p1, const T p2);
void first() const;
void second() const;
};
#endif
////////////////////////////////////////////////////////////////////////////////
// pair.cxx
#include <iostream.h>
#include "pair.h"
template<class T>
pair<T>::pair()
{
item1 = (T)NULL;
item2 = (T)NULL;
}
template<class T>
pair<T>::pair(const T p1, const T p2)
{
item1 = p1;
item2 = p2;
}
template<class T>
void pair<T>::first() const
{
cout << item1 << endl;
}
template<class T>
void pair<T>::second() const
{
cout << item2 << endl;
}
#ifdef NOAUTO
#pragma define_template pair<int>
#pragma define_template pair<char>
#endif
////////////////////////////////////////////////////////////////////////////////
// prog.cxx
#include <iostream.h>
#include "pair.h"
void main()
{
pair<int> int_pair(1,2);
pair<char> char_pair('a','b');
int_pair.first();
int_pair.second();
char_pair.first();
char_pair.second();
}
////////////////////////////////////////////////////////////////////////////////
% rm *auto*
% rm -r cxx_repository
% time make prog_noauto
cxx -c -o pair.o -DNOAUTO pair.cxx
cxx -o prog_noauto -nopt -DNOAUTO prog.cxx pair.o
2.43u 0.60s 0:03 93% 5+14k 0+0io 0pf+0w
% time make prog_auto
cxx -o prog_auto -pt prog.cxx
3.54u 1.03s 0:04 93% 5+14k 0+0io 0pf+0w
% prog_auto
1
2
a
b
% prog_noauto
1
2
a
b
================================================================================
|