DEC C++
Using DEC C++ for DIGITAL UNIX Systems


Previous Contents Index

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.a, 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 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.

Note

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, DIGITAL 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, DEC 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, DEC C++ performs the prelink steps associated with automatic template instantiation. To avoid automatic template instantiation, specify the -nopt option to the cxx command.

5.3 Manual Instantiation

DEC 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:

#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> > 

5.3.1 Using the define_template Command-Line Option

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

5.3.2 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, DEC 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

DEC 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. DEC 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