11.2. Shared objects with templates

Templates are usually declared in a header file. Each time a template is used, code is generated to instantiate the template with the desired parameters. Most C++ compilers work with a template repository. No template code is generated at compile time; the compiler just remembers where the template code came from. Then, at link time, as the compiler/linker puts all parts together, it notices which templates actually need to be generated. The code is then produced, compiled, and linked into the application.

This becomes a problem when using templates with shared libraries, where no actual linking takes place. Therefore, you must make sure that the template code is generated when producing the shared library.

Therefore, one should keep track of compilation and inclusion of template instantiations. This would mean that one has to manually keep track of all the template instantiation and address them during the linking phase.

Starting from Version 5, the VisualAge C++ for AIX product supports a compiler option, -qmkshrobj, to create a shared objects. This option, together with the -qtempinc option, should be used in preference to the makeC++SharedLib command when creating a shared object that uses templates. The advantage of using these options instead of makeC++SharedLib is that the compiler will automatically include and compile the template instantiations in the tempinc directory.

11.2.1. Templates and makeC++SharedLib

The makeC++SharedLib command is supplied with all IBM C++ command line compiler drivers for the AIX platform.[2] The command is implemented as a shell script that gathers the supplied input and then calls the linker to create the shared object.

[2] Type ls -l /usr/vacpp/bin/makeC++SharedLib* on the command line.

When creating a shared object that uses templates, the makeC++SharedLib command needs to somehow find information on the templates that are to be instantiated. Because the script calls the linker, and not the compiler, it does not look at the contents of the tempinc directory. This means the method of creating a shared object that uses templates relies on either using the simple template method code layout, as described in 10.3, “Simple code layout method” on page 381, or forcing templates to be instantiated, as described in 10.4.3, “Forcing template instantiation” on page 386.

The best method to use will depend on the circumstances. Using the simple code layout method means that all the required templates are generated automatically.

However, it also comes with the disadvantages of slower compile times and larger code size. Forcing the templates to be instantiated is better from both the code size and compile time aspect, but it does mean that the user needs to maintain files that instantiate the required templates.

Suppose you want to create a shared object from the following two source files, which use the preferred code layout method.

File source1.C contains the following code:

#include "stack.h"
stack<int> counterl;

void function1(int a)
{
    counter1.push(a);
}

The file source2.C contains the following code:

include "stack.h"
stack<int> counter2;

void function2(int a)
{
    counter2.push(a);
}

Using the makeC++SharedLib command, an attempt is made to create a shared object as follows:

$ xlC -c source1.C source2.C
$ /usr/vacpp/bin/makeC++SharedLib -o shr1.o -p0 source1.o source2.o
ld: 0711-317 ERROR: Undefined symbol: .stack<int>::stack(int)
ld: 0711-317 ERROR: Undefined symbol: .stack<int>::~stack()
ld: 0711-317 ERROR: Undefined symbol: .stack<int>::push(int)
ld: 0711-345 Use the -bloadmap or -bnoquiet option to obtain more information.

The command failed, and based on the output, it is easy to see that the required template functions have not been instantiated. At this point, note that because the code uses the preferred code layout method, the compiler has, in fact, automatically created the file tempinc/stack.C, which, if compiled, would supply the required template definitions. You can, if you wish, copy this file and make it an explicit compilation unit as part of your source code. In this case, that would mean adding the following command to the sequence:

$ xlC -c tempinc/stack.C -o stack.o

The object, file stack.o, would then be passed to the makeC++SharedLib command along with source1.o and source2.o.

11.2.2. Templates and -qmkshrobj

You should use the -qmkshrobj option instead of the makeC++SharedLib command when creating a shared object. Because the option is a compiler option, the compiler will automatically look in the tempinc directory (or the directory specified with the -qtempinc=dirname option) for the automatically generated template instance information. Using the same source files as described in the previous section, the following commands can be used to create the shared object:

$ xlC -c source1.C source2.C
$ xlC -qmkshrobj -o shr1.o source1.o source2.o

This time, the command works correctly, since the compiler looks in the tempinc directory. Remember to use the same -qtempinc option (if any) that was used when compiling the modules being used to create the shared object.

This option solves the problems associated with creating shared objects that use template classes and functions. If you want to create a shared object that contains pre-instantiated template classes and functions for use by other developers, then you can create an additional compilation unit that explicitly defines the required templates using the #pragma define directive.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset