C H A P T E R  7

Compiling Templates

Template compilation requires the C++ compiler to do more than traditional UNIX compilers have done. The C++ compiler must generate object code for template instances on an as-needed basis. It might share template instances among separate compilations using a template repository. It might accept some template compilation options. It must locate template definitions in separate source files and maintain consistency between template instances and mainline code.


7.1 Verbose Compilation

When given the flag -verbose=template, the C++ compiler notifies you of significant events during template compilation. Conversely, the compiler does not notify you when given the default, -verbose=no%template. The +w option might give other indications of potential problems when template instantiation occurs.


7.2 Template Instantiation

The CCadmin(1) command administers the template repository. For example, changes in your program can render some instantiations superfluous, thus wasting storage space. The CCadmin -clean command (formerly ptclean) clears out all instantiations and associated data. Instantiations are recreated only when needed.

7.2.1 Generated Instances

The compiler treats inline template functions as inline functions for the purposes of template instance generation. The compiler manages them as it does other inline functions, and the descriptions in this chapter do not apply to template inline functions.

7.2.2 Whole-Class Instantiation

The compiler usually instantiates members of template classes independently of other members, so that the compiler instantiates only members that are used within the program. Methods written solely for use through a debugger will therefore not normally be instantiated.

There are two means to ensure that debugging members are available to the debugger.

The ISO C++ Standard permits developers to write template classes for which all members may not be legal with a given template argument. As long as the illegal members are not instantiated, the program is still well formed. The ISO C++ Standard Library uses this technique. However, the -template=wholeclass option instantiates all members, and hence cannot be used with such template classes when instantiated with the problematic template arguments.

7.2.3 Compile-Time Instantiation

Instantiation is the process by which a C++ compiler creates a usable function or object from a template. The C++ compiler uses compile-time instantiation, which forces instantiations to occur when the reference to the template is being compiled.

The advantages of compile-time instantiation are:

Templates can be instantiated multiple times if source files reside in different directories or if you use libraries with template symbols.

7.2.4 Template Instance Placement and Linkage

Beginning with version 5.5 of Sun's C++ compiler, instances go into special address sections, and the linker recognizes and discards duplicates. You can instruct the compiler to use one of five instance placement and linkage methods: external, static, global, explicit, and semi-explicit.

This section discusses the five instance placement and linkage methods. Additional information about generating instances can be found in Section 6.3, Template Instantiation.


7.3 External Instances

With the external instances method, all instances are placed within the template repository. The compiler ensures that exactly one consistent template instance exists; instances are neither undefined nor multiply defined. Templates are reinstantiated only when necessary. For non-debug code, the total size of all object files (including any within the template cache) may be smaller with -instances=extern than with -instances=global.

Template instances receive global linkage in the repository. Instances are referenced from the current compilation unit with external linkage.



Note - If you are compiling and linking in separate steps and you specify -instance=extern for the compilation step, you must also specify it for the link step.



The disadvantage of this method is that the cache is subject to corruption, and must be cleared whenever changing programs or making significant program changes. The cache is a bottleneck for parallel compilation, as when using dmake because access to the cache must be restricted to one compilation at a time. Also, you can only build one program within a directory.

It can take longer to determine whether a valid template instance is already in the cache than just to create the instance in the main object file and discard it later if needed.

Specify external linkage with the --instances=extern option.

Because instances are stored within the template repository, you must use the CC command to link C++ objects that use external instances into programs.

If you wish to create a library that contains all template instances that it uses, use the CC command with the --xar option. Do not use the ar command. For example:

example% CC -xar -instances=extern -o libmain.a a.o b.o c.o

See Chapter 16 for more information.

7.3.0.1 Possible Cache Conflicts

Do not run different compiler versions in the same directory due to possible cache conflicts when you specify -instance=extern. Consider the following when you use the -instances=extern template model:

7.3.1 Static Instances



Note - The -instances=static option is deprecated. There is no longer any reason to use -instances=static, because -instances=global now gives you all the advantages of static without the disadvantages. This option was provided in earlier compilers to overcome problems that do not exist in C++ 5.5.



With the static instances method, all instances are placed within the current compilation unit. As a consequence, templates are reinstantiated during each recompilation; instances are not saved to the template repository.

The advantage of this method is that it can help with debugging by ensuring that the instances called from a compilation unit are compiled with that compilation unit, and are thus debugable. With -instances=global, you don't have that guarantee.

The disadvantage of this method is that it does not follow language semantics and makes substantially larger objects and executables.

Instances receive static linkage. These instances will not be visible or usable outside the current compilation unit. As a result, templates might have identical instantiations in several object files. Because multiple instances produce unnecessarily large programs, static instance linkage is suitable only for small programs, where templates are unlikely to be multiply instantiated.

Compilation is potentially faster with static instances, so this method might also be suitable during Fix-and-Continue debugging. (See Debugging a Program With dbx.)



Note - If your program depends on sharing template instances (such as static data members of template classes or template functions) across compilation units, do not use the static instances method. Your program will not work properly.



Specify static instance linkage with the --instances=static compiler option.

7.3.2 Global Instances

Unlike previous compiler releases, it is no longer necessary to guard against multiple copies of a global instance.

The advantage of this method is that incorrect source code commonly accepted by other compilers is now also accepted in this mode. In particular, references to static variables from within a template instances are not legal, but commonly accepted.

The disadvantage of this method is that individual object files may be larger, due to copies of template instances in multiple files. If you compile some object files for debug using the -g option, and some without, it is hard to predict whether you will get a debug or non-debug version of a template instance linked into the program.

Template instances receive global linkage. These instances are visible and usable outside the current compilation unit.

Specify global instances with the --instances=global option (this is the default).

7.3.3 Explicit Instances

In the explicit instances method, instances are generated only for templates that are explicitly instantiated. Implicit instantiations are not satisfied. Instances are placed within the current compilation unit. As a consequence, templates are reinstantiated during each recompilation; they are not saved to the template repository.

The advantage of this method is that you no longer need to ensure that only one explicit instance is ever generated. You have the least amount of template compilation and smallest object sizes.

The disadvantage is that you must perform all instantiation manually.

Template instances receive global linkage. These instances are visible and usable outside the current compilation unit. The linker recognizes and discards duplicates.

Specify explicit instances with the --instances=explicit option.

7.3.4 Semi-Explicit Instances

When you use the semi-explicit instances method, instances are generated only for templates that are explicitly instantiated or implicitly instantiated within the body of a template. Instances required by explicitly-created instances are generated automatically but given internal (static) linkage. Implicit instantiations in the mainline code are not satisfied. Instances are placed within the current compilation unit. As a consequence, templates are reinstantiated during each recompilation; they are not saved to the template repository.

Instances receive global linkage. These instances are visible and usable outside the current compilation unit. The linker recognizes and discards duplicates.

Specify semi-explicit instances with the --instances=semiexplicit option.


7.4 The Template Repository

The template repository stores template instances between separate compilations so that template instances are compiled only when it is necessary. The template repository contains all nonsource files needed for template instantiation when using the external instances method. The repository is not used for other kinds of instances.

7.4.1 Repository Structure

The template repository is contained, by default, within a cache directory called SunWS_cache.

The cache directory is contained within the directory in which the object files are placed. You can change the name of the cache directory by setting the SUNWS_CACHE_NAME environment variable. Note that the value of the SUNWS_CACHE_NAME variable must be a directory name and not a path name. This is because the compiler automatically places the template cache directory under the object file directory so the compiler already has a path.

7.4.2 Writing to the Template Repository

When the compiler must store template instances, it stores them within the template repository corresponding to the output file. For example, the following command line writes the object file to ./sub/a.o and writes template instances into the repository contained within ./sub/SunWS_cache. If the cache directory does not exist, and the compiler needs to instantiate a template, the compiler will create the directory.

example% CC -o sub/a.o a.cc

7.4.3 Reading From Multiple Template Repositories

The compiler reads from the template repositories corresponding to the object files that it reads. That is, the following command line reads from ./sub1/SunWS_cache and ./sub2/SunWS_cache, and, if necessary, writes to ./SunWS_cache.

example% CC sub1/a.o sub2/b.o

7.4.4 Sharing Template Repositories

Templates that are within a repository must not violate the one-definition rule of the ISO C++ standard. That is, a template must have the same source in all uses of the template. Violating this rule produces undefined behavior.

The simplest, though most conservative, way to ensure that the rule is not violated is to build only one program or library within any one directory. Two unrelated programs might use the same type name or external name to mean different things. If the programs share a template repository, template definitions could conflict, thus yielding unpredictable results.

7.4.5 Template Instance Automatic Consistency With -instance=extern

The template repository manager ensures that the states of the instances in the repository are consistent and up-to-date with your source files when you specify -instances=extern.

For example, if your source files are compiled with the -g option (debugging on), the files you need from the database are also compiled with -g.

In addition, the template repository tracks changes in your compilation. For example, if you have the --DDEBUG flag set to define the name DEBUG, the database tracks this. If you omit this flag on a subsequent compile, the compiler reinstantiates those templates on which this dependency is set.


7.5 Template Definition Searching

When you use the definitions-separate template organization, template definitions are not available in the current compilation unit, and the compiler must search for the definition. This section describes how the compiler locates the definition.

Definition searching is somewhat complex and prone to error. Therefore, you should use the definitions-included template file organization if possible. Doing so helps you avoid definition searching altogether. See Section 5.2.1, Template Definitions Included.



Note - If you use the -template=no%extdef option, the compiler will not search for separate source files.



7.5.1 Source File Location Conventions

Without the specific directions provided with an options file, the compiler uses a Cfront-style method to locate template definition files. This method requires that the template definition file contain the same base name as the template declaration file. This method also requires that the template definition file be on the current include path. For example, if the template function foo() is located in foo.h, the matching template definition file should be named foo.cc or some other recognizable source-file extension (.C, .c, .cc, .cpp, .cxx, or .c++). The template definition file must be located in one of the normal include directories or in the same directory as its matching header file.

7.5.2 Definitions Search Path

As an alternative to the normal search path set with -I, you can specify a search directory for template definition files with the option -ptidirectory. Multiple -pti flags define multiple search directories--that is, a search path. If you use -ptidirectory, the compiler looks for template definition files on this path and ignores the -I flag. Since the -ptidirectory flag complicates the search rules for source files, use the -I option instead of the -ptidirectory option.


7.6 Template Options File

The template options file is a user-provided optional file that contains the options needed to locate template definitions and to control instance recompilation. In addition, the options file provides features for controlling template specialization and explicit instantiation. However, because the C++ compiler now supports the syntax required to declare specializations and explicit instantiation in the source code, you should not use these features.



Note - The template options file will not be supported in future releases of the C++ compiler.



The options file is named CC_tmpl_opt and resides within the SunWS_config directory. The options file is an ASCII text file containing a number of entries. An entry consists of a keyword followed by expected text and terminated with a semicolon (;). Entries can span multiple lines, although the keywords cannot be split.

7.6.1 Comments

Comments start with a # character and extend to the end of the line. Text within a comment is ignored.

# Comment text is ignored until the end of the line.

7.6.2 Includes

You may share options files among several template databases by including the options files. This facility is particularly useful when building libraries containing templates. During processing, the specified options file is textually included in the current options file. You can have more than one include statement and place them anywhere in the options file. The options files can also be nested.

include "options-file";

7.6.3 Source File Extensions

You can specify different source file extensions for the compiler to search for when the compiler is using its default Cfront-style source-file-locator mechanism. The format is:

extensions "ext-list";

The ext-list is a list of extensions for valid source files in a space-separated format such as:

extensions ".CC .c .cc .cpp";

In the absence of this entry from the options file, the valid extensions for which the compiler searches are .cc, .c, .cpp, .C, .cxx, and .c++.

7.6.4 Definition Source Locations

You can explicitly specify the locations of definition source files using the definition option file entry. Use the definition entry when the template declaration and definition file names do not follow the standard Cfront-style conventions. The entry syntax is:

definition name in "file-1",[ "file-2" ..., "file-n"] [nocheck "options"];

The name field indicates the template for which the option entry is valid. Only one definition entry per name is allowed. That name must be a simple name; qualified names are not allowed. Parentheses, return types, and parameter lists are not allowed. Regardless of the return type or parameters, only the name itself counts. As a consequence, a definition entry may apply to several (possibly overloaded) templates.

The "file-n" list field specifies the files that contain the template definitions. The search for the files uses the definition search path. The file names must be enclosed in quotes (" "). Multiple files are available because the simple template name may refer to different templates defined in different files, or because a single template may have definitions in multiple files. For example, if func is defined in three files, then those three files must be listed in the definition entry.

The nocheck field is described at the end of this section.

In the following example, the compiler locates the template function foo in foo.cc, and instantiates it. In this case, the definition entry is redundant with the default search.

CODE EXAMPLE 7-1 Redundant Definition Entry

foo.cc

template <class T> T foo( T t ) { }

CC_tmpl_opt

definition foo in "foo.cc";

The following example shows the definition of static data members and the use of simple names.

CODE EXAMPLE 7-2 Definition of Static Data Members and Use of Simple Names

foo.h

template <class T> class foo { static T* fooref; };

foo_statics.cc

#include "foo.h"
template <class T> T* foo<T>::fooref = 0

CC_tmpl_opt

definition fooref in "foo_statics.cc";

The name provided for the definition of fooref is a simple name and not a qualified name (such as foo::fooref). The reason for the definition entry is that the file name is not foo.cc (or some other recognizable extension) and cannot be located using the default Cfront-style search rules.

The following example shows the definition of a template member function. As the example shows, member functions are handled exactly like static member initializers.

CODE EXAMPLE 7-3 Template Member Function Definition

foo.h

template <class T> class foo { T* foofunc(T); };

foo_funcs.cc

#include "foo.h"
template <class T> T* foo<T>::foofunc(T t) {}

CC_tmpl_opt

definition foofunc in "foo_funcs.cc";

The following example shows the definition of template functions in two different source files.

CODE EXAMPLE 7-4 Definition of Template Functions in Different Source Files

foo.h

template <class T> class foo {
    T* func( T t );
    T* func( T t, T x );
};

foo1.cc

#include "foo.h"
template <class T> T* foo<T>::func( T t ) { }

foo2.cc

#include "foo.h"
template <class T> T* foo<T>::func( T t, T x ) { }

CC_tmpl_opt

definition func in "foo1.cc", "foo2.cc";

In this example, the compiler must be able to find both of the definitions of the overloaded function func(). The definition entry tells the compiler where to find the appropriate function definitions.

Sometimes recompiling is unnecessary when certain compilation flags change. You can avoid unnecessary recompilation using the nocheck field of the definition option file entry, which tells the compiler and template database manager to ignore certain options when checking dependencies. If you do not want the compiler to reinstantiate a template function because of the addition or deletion of a specific command-line flag, use the nocheck flag. The entry syntax is:

definition name in "file-1"[, "file-2" ..., "file-n"] [nocheck "options"];

The options must be enclosed in quotes (" ").

In the following example, the compiler locates the template function foo in foo.cc, and instantiates it. If a reinstantiation check is later required, the compiler will ignore the -g option.

CODE EXAMPLE 7-5 nocheck Option

foo.cc

template <class T> T foo( T t ) {}

CC_tmpl_opt

definition foo in "foo.cc" nocheck "-g";

7.6.5 Template Specialization Entries

Until recently, the C++ language provided no mechanism for specializing templates, so each compiler provided its own mechanism. This section describes the specialization of templates using the mechanism of previous versions of the C++ compilers. This mechanism is only supported in compatibility mode (-compat[=4]).

The special entry tells the compiler that a given function is a specialization and should not be instantiated when the compiler encounters the function. When using the compile-time instantiation method, use special entries in the options file to preregister the specializations. The syntax is:

special declaration;

The declaration is a legal C++-style declaration without return types. For example:

CODE EXAMPLE 7-6 special Entry

foo.h

template <class T> T foo( T t ) { };

main.cc

#include "foo.h"

CC_tmpl_opt

special foo(int);

The preceding options file informs the compiler that the template function foo() should not be instantiated for the type int, and that a specialized version is provided by the user. Without that entry in the options file, the function may be instantiated unnecessarily, resulting in errors:

CODE EXAMPLE 7-7 Example of When special Entry Should Be Used

foo.h

template <classT> T foo( T t ) { return t + t; }

file.cc

#include "foo.h"
int func( ) { return foo( 10 ); }

main.cc

#include "foo.h"
int foo( int i ) { return i * i; } // the specialization
int main( ) { int x = foo( 10 ); int y = func(); 
return 0; }

In the preceding example, when the compiler compiles main.cc, the specialized version of foo is correctly used because the compiler has seen its definition. When file.cc is compiled, however, the compiler instantiates its own version of foo because it doesn't know foo exists in main.cc. In most cases, this process results in a multiply-defined symbol during the link, but in some cases (especially libraries), the wrong function may be used, resulting in runtime errors. If you use specialized versions of a function, you should register those specializations.

The special entries can be overloaded, as in this example:

CODE EXAMPLE 7-8 Overloading special Entries

foo.h

template <classT> T foo( T t ) {}

main.cc

#include "foo.h"
int   foo( int   i ) {}
char* foo( char* p ) {}

CC_tmpl_opt

special foo(int);
special foo(char*);

To specialize a template class, include the template arguments in the special entry:

CODE EXAMPLE 7-9 Specializing a Template Class

foo.h

template <class T> class Foo { ... various members ... };

main.cc

#include "foo.h"
int main( ) { Foo<int> bar; return 0; }

CC_tmpl_opt

special class Foo<int>;

If a template class member is a static member, you must include the keyword static in your specialization entry:

CODE EXAMPLE 7-10 Specializing a Static Template Class Member

foo.h

template <class T> class Foo { public: static T func(T); };

main.cc

#include "foo.h"
int main( ) { Foo<int> bar; return 0; }

CC_tmpl_opt

special static Foo<int>::func(int);