United States    
COMPAQ STORE | PRODUCTS |
SERVICES | SUPPORT | CONTACT US | SEARCH
cxxtitle.gif (12116 bytes)
Compaq C++

Compaq C++
Using Compaq C++ for Tru64 UNIX Systems


Previous Contents Index

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.

Your Library Sources

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:

Building Your Library Sources

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.

A Sample Build Procedure

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.

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.

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.

5.2.9.2 Building an Application

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.

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.

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:

A Sample Build Procedure

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:


                         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 

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


Previous Next Contents Index
  

1.800.AT.COMPAQ

privacy and legal statement