Changing source code to support link time template instantiation

Article ID: 1170
Last updated: 31 Jan, 2008
Article ID: 1170
Last updated: 31 Jan, 2008
Revision: 1
Views: 3480
Posted: 24 Feb, 2000
by Dean J.
Updated: 31 Jan, 2008
by Dean J.
Problem


Link time template instantiation is not supported on DEC or AIX


Cause


First, a background on template instantiation. In C++, there are two major rules to building a binary object:

  • All symbols must be accounted for somewhere in an executable.
  • All symbols must either be accounted for in a library otherwise they will have to be resolved (i.e. accounted for) later to satisfy the above condition

A symbol is nothing more than a name of a variable, a function, a class, template, etc. Every symbol in C++ must have two parts: a declaration and a definition.

  • A declaration is a prototype or a way to tell C++ that the symbol will be used in the future.
  • A definition is the actual implementation (i.e. body of code that does something).
The build process of an executable is something like this:

  1. Compile all .cpp and .h files into object files (.o or .obj). Many symbols can be left unresolved, to be resolved later at link time.
  2. Building a static library will take all of these object files and package them together into a library file (.a or .lib). No symbol resolution is done. If any of the object files have unresolved symbols, they will not surface until the library is used to build an executable (see below).
  3. Building a shared library will attempt to resolve all of the symbols from the object files and create an executable library (.so or .dll). This is different than a static library, because the library should have all of its symbols resolved when it is built.
  4. Building an executable will take all of the symbols declared in the object files and libraries and try to find the definition of the symbol in any of the object or library files linked in. This is the source of unresolved symbol errors. This means a symbol was declared or used without having a definition body (i.e. C++ doesn't have a block of code to run when this symbol is used).

The above works great for taking regular C++ code and putting them into libraries and executables. Templates mess things up a bit. Templates are code constructs that allow code reuse. One bit of code can be templatized on a variety of different data types (int, char, even other classes like RWTValHashMap). This means that the compiler has to define the code for every different version of the template that gets declared (i.e. An RWTValSortedVec and a RWTValSortedVec both have to be defined) so that those symbols can be resolved at link time. The compiler can either do this as it is compiling (Compile time instantiation - CTI) or as it is linking and it finds the symbols (link time instantiation - LTI).

CTI is simpler. Basically the code for the templates gets included into the rest of the compiling code via includes (the preprocessor just cuts and pastes the text in the included file at the point that it is included). This ends up making all of the object files bigger, and thus the resulting library is bigger.

LTI is more complex. The compiler will compile all of the template files separately and then has to figure out which templates are called for and it only links in those object files. This means that the template code is not all included. Thus, the library is much smaller. This means that the compiler must be smart enough to know how to find templates that need to be resolved and know how to find the file that the definition is in. This also means that the compiler must compile these files and link them into the main project, so that all unresolved symbols are taken care of.

The above choice is taken care of by a Roguewave macro called RW_COMPILE_INSTANTIATE. If this is set to 1 the compiler will just include all of the template files (usually .cc extension) in the header files. If it is 0 the compiler will have to take care of this. The process for compilation with LTI follows:

  1. Compile regular .cpp files
  2. Compile all template (.cc) files
  3. Find which templates are needed
  4. Only link in the code for those templates
  5. Finish building target (executable or library)

Most compilers can accomplish the above if it is building an executable. Some compilers have problems if the Roguewave code is in a static library. This is because the compiler uses a utility called ar to put the object files together. Ar is a dumb archiver that does not compile or link in the template files. Thus, the static library will have many unresolved symbols, since the template code is not included. In these cases, the user needs to do the above steps manually, or the user must use CTI.

Other compilers (such as IBM xlC and DEC cxx) cannot figure out which template files that it needs to compile. They both do fine in linking in the symbols, but they can't compile the template files without help.


Action


Now that we've covered the basic problem, fixing it is the easy part.
Just follow the following steps to enable link time instantiation on Digital or AIX.

1a. First modify the makefile.in file for each product. The makefile.in file is located under your
/parts//source/etc/PC or UNIX// For example, spm12/parts/thr0140u/source/etc/UNIX/SOLARIS/SUNPRO b. Remove the comment from line number 134, i.e. change from #RWTEMPLATES_NEEDED=tmp.timestamp to RWTEMPLATES_NEEDED=tmp.timestamp c. Change line 138 from RWTEMPLATE_REPOSITORY= to RWTEMPLATE_REPOSITORY=tempinc RWTEMPLATEOBJS=tempinc/*.o d. If the following lines of code are NOT included nor found, then add them before clean: in the current product's makefile.in. #################### Internal Templates Target ####################### # The following target creates a repository of all templates used in # this product. The objects in this repository are explicitly added to # the library for compilers which require this. tmp.timestamp: $(RWOBJECTS) @echo Instantiating ... ignore undefined symbol errors -$(RWCPPINVOKE) $(RWCPPFLAGS) -L$(RWWORKSPACE)/lib -o tempfiLe $(RWOBJECTS) $(RWLINKLIBS) -$(RWRM) tempfile $(RWTOUCH) $@ 2a. Next you will need to find the product's template files. These will be located in the parts/product/source/src/rw/product header tree directory, e.g. Threads template files are located in parts/thr0130u/source/src/rw/thr. The template files will be the files with the .cc extension. b. Edit the corresponding .h of these files one by one (e.g. for escrothr.cc, edit escrothr.h). At the bottom of the file, put the following statements: ( Note: Not all *.h files have a corresponding *.cc file. You may want to use the ls command to view the complete list of files in your rw directories with the .cc extension. ) #if defined(_AIX) && !defined(RW_COMPILE_INSTANTIATE) #pragma implementation(escrothr.cc) #endif This will tell the compiler where the template instantiation file is for that .h file. 3. Edit the spmroot/scripts/comptest/test025 file. Remove line 37, AIX | DIGITAL) RW_COMPILE_INSTANTIATE=1 ;; , from the test025 file. This will force RW_COMPILE_INSTANTIATE to get set to 0. Build the product and it should build the library fine. Then try the examples to make sure. This should be all there is to it. Please note that these changes are not supported by Roguewave. Use these changes as a starting point. They may or may not completely work for you. The above changes should work on AIX. The above changes have not been tested on DEC. Again, these changes are not supported. Please use at your own discretion.
This article was:   Helpful | Not helpful
Report an issue
Article ID: 1170
Last updated: 31 Jan, 2008
Revision: 1
Views: 3480
Posted: 24 Feb, 2000 by Dean J.
Updated: 31 Jan, 2008 by Dean J.

Others in this category