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 Repository Administration

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 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. 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 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 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. 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; instances receive global linkage and they are not saved to the template repository.

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 -instances=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.5.3 Troubleshooting a Problematic Search

Sometimes the compiler generates confusing warnings or error messages because it is looking for file that you don't intend to compile. Usually, the problem is that a file, for example foo.h, contains template declarations and another file, such as foo.cc, gets implicitly included.

If a header file, foo.h, has template declarations, the compiler searches for a file called foo with a C++ file extension (.C, .c, .cc, .cpp, .cxx, or .c++) by default. If the compiler finds such a file, it includes the file automatically. See Section 7.5, Template Definition Searching for more information on such searches.

If you have a file foo.cc that you don't intend to be treated this way, you have two options: