24.7 Implementation Details

I l @ ve RuBoard

Now we come to a problem that has vexed most compiler makers , the details of the compilation process. Consider the files that make up Example 24-3. The program defines the following files listed in Examples Example 24-3 through Example 24-6.

integer.h

Defines a simple integer class.

square.h

Defines the prototype for the template function square.

square.cpp

Defines the body of the template function square.

main.cpp

Uses the template function square with the class integer.

Example 24-3. template/integer.h
 class integer  {     public:         int value;         integer(int i_value): value(i_value) {};         integer operator * (const integer i2)         {             integer result(value * i2.value);             return (result);         } }; 
Example 24-4. File: template/square.h
 template<class number> extern number square(const number value); 
Example 24-5. File: template/main.cpp
 #include "square.h" #include "integer.h" int main(  ) {     integer test(5);     integer test2 = square(test);     return (0); } 
Example 24-6. File: template/square.cpp
 #include "square.h" export  template<class number> number square(const number i) {     return (i.value * i.value); } 

Now consider what must happen when we compile these files. We'll start with file main.cpp . When this file is compiled, the compiler sees that the template function square is used with the class integer . Normally, this would cause it to generate the code for square<integer> . But there is a problem: the body of this function is defined in the file square.cpp , which we are not compiling at this time. The result is that the compiler doesn't have enough information to generate the template.

But when we compile square.cpp , the compiler knows nothing about the class integer , so it can't generate the code for square<integer> either.

The C++ Standard has a solution to this problem. The solution is to put the keyword export in front of the definition of the function square in the file square.cpp . This tells the C++ compiler, "This template definition may be used in other files so keep its code around to be expanded in later compilations."

So the first thing we do is to compile square.cpp . The export directive tells the compiler to save a definition of the function somewhere. Next we compile main.cpp . The compiler sees that we want to generate code for square<integer> , goes to its library of exported templates, looks for the definition square , and uses it to generate the code.

24.7.1 Real-World Templates

Unfortunately, the standards for handling templates were one of the last things to be defined in the process of creating the C++ Standard. As a result, many compilers cannot compile standard code.

Most compilers force you to define all the data types you're going to use with a template before you define the body of a template. In our example, this means that we must include the line:

 #include "integer.h" 

in the square.cpp file. We also must tell C++ that we want to generate a function for square<integer> through the use of the statement:

 template integer square<integer>(const integer value); 

So our updated square.cpp would look like this:

Example 24-7. template/square2.cpp
 #include "square.h" #include "integer.h" template integer square<integer>(const integer i); template<class number> number square(const number i) {     return (i.value * i.value); } 

24.7.2 When to Generate Code

Suppose we have 20 files, all of which use the template function max<int> . On some compilers, this means that the body of the function is generated for every file in which it is used (in this case, the function is generated 20 times). That also means that the code for the function body is loaded 20 times in our program. That's 19 times too many.

Some compilers are smart enough to detect multiple loading of the same function and delete the extras. Some are not, and the result can be very large object files.

To solve this problem, some compiler makers force you to provide hints in your code to tell them when to generate a template and when not to. The actual syntax for the hints varies from compiler to compiler, so check your compiler documentation.

24.7.3 Writing Portable Templates

How can you write a portable template? One way to create a truly portable template is to write all your templates as inline functions and put all your functions in a single header file. As far as I can tell, this method works for every compiler that has templates. It may not be the most efficient way of doing things, but it is the most portable.

I l @ ve RuBoard


Practical C++ Programming
Practical C Programming, 3rd Edition
ISBN: 1565923065
EAN: 2147483647
Year: 2003
Pages: 364

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net