- •Introduction
- •Saving Time with This Book
- •Conventions Used in This Book
- •Part II: Working with the Pre-Processor
- •Part III: Types
- •Part IV: Classes
- •Part V: Arrays and Templates
- •Part VI: Input and Output
- •Part VII: Using the Built-in Functionality
- •Part VIII: Utilities
- •Part IX: Debugging C++ Applications
- •Part X: The Scary (or Fun!) Stuff
- •Icons Used in This Book
- •Creating and Implementing an Encapsulated Class
- •Creating a Mailing-List Application
- •Testing the Mailing-List Application
- •Customizing a Class with Polymorphism
- •Testing the Virtual Function Code
- •Why Do the Destructors Work?
- •Delayed Construction
- •The cDate Class
- •Testing the cDate Class
- •Creating the Header File
- •Testing the Header File
- •The Assert Problem
- •Fixing the Assert Problem
- •Using the const Construct
- •Identifying the Errors
- •Fixing the Errors
- •Fixing What Went Wrong with the Macro
- •Using Macros Appropriately
- •Using the sizeof Function
- •Evaluating the Results
- •Using sizeof with Pointers
- •Implementing the Range Class
- •Testing the Range Class
- •Creating the Matrix Class
- •Matrix Operations
- •Multiplying a Matrix by a Scalar Value
- •Multiplying a Matrix by Scalar Values, Take 2
- •Testing the Matrix Class
- •Implementing the Enumeration Class
- •Testing the Enumeration Class
- •Implementing Structures
- •Interpreting the Output
- •Defining Constants
- •Testing the Constant Application
- •Using the const Keyword
- •Illustrating Scope
- •Interpreting the Output
- •Using Casts
- •Addressing the Compiler Problems
- •Testing the Changes
- •Implementing Member-Function Pointers
- •Updating Your Code with Member-Function Pointers
- •Testing the Member Pointer Code
- •Customizing Functions We Wrote Ourselves
- •Testing the Default Code
- •Fixing the Problem
- •Testing the Complete Class
- •Implementing Virtual Inheritance
- •Correcting the Code
- •Rules for Creating Overloaded Operators
- •Using Conversion Operators
- •Using Overloaded Operators
- •Testing the MyString Class
- •Rules for Implementing new and delete Handlers
- •Overloading new and delete Handlers
- •Testing the Memory Allocation Tracker
- •Implementing Properties
- •Testing the Property Class
- •Implementing Data Validation with Classes
- •Testing Your SSN Validator Class
- •Creating the Date Class
- •Testing the Date Class
- •Some Final Thoughts on the Date Class
- •Creating a Factory Class
- •Testing the Factory
- •Enhancing the Manager Class
- •Implementing Mix-In Classes
- •Testing the Template Classes
- •Implementing Function Templates
- •Creating Method Templates
- •Using the Vector Class
- •Creating the String Array Class
- •Working with Vector Algorithms
- •Creating an Array of Heterogeneous Objects
- •Creating the Column Class
- •Creating the Row Class
- •Creating the Spreadsheet Class
- •Testing Your Spreadsheet
- •Working with Streams
- •Testing the File-Reading Code
- •Creating the Test File
- •Reading Delimited Files
- •Testing the Code
- •Creating the XML Writer
- •Testing the XML Writer
- •Creating the Configuration-File Class
- •Setting Up Your Test File
- •Building the Language Files
- •Creating an Input Text File
- •Reading the International File
- •Testing the String Reader
- •Creating a Translator Class
- •Testing the Translator Class
- •Creating a Virtual File Class
- •Testing the Virtual File Class
- •Using the auto_ptr Class
- •Creating a Memory Safe Buffer Class
- •Throwing and Logging Exceptions
- •Dealing with Unhandled Exceptions
- •Re-throwing Exceptions
- •Creating the Wildcard Matching Class
- •Testing the Wildcard Matching Class
- •Creating the URL Codec Class
- •Testing the URL Codec Class
- •Testing the Rot13 Algorithm
- •Testing the XOR Algorithm
- •Implementing the transform Function to Convert Strings
- •Testing the String Conversions
- •Implementing the Serialization Interface
- •Creating the Buffer Class
- •Testing the Buffer Class
- •Creating the Multiple-Search-Path Class
- •Testing the Multiple-Search-Path Class
- •Testing the Flow Trace System
- •The assert Macro
- •Logging
- •Testing the Logger Class
- •Design by Contract
- •Adding Logging to the Application
- •Making Functions Inline
- •Avoiding Temporary Objects
- •Passing Objects by Reference
- •Choosing Initialization Instead of Assignment
- •Learning How Code Operates
- •Testing the Properties Class
- •Creating the Locking Mechanism
- •Testing the Locking Mechanism
- •Testing the File-Guardian Class
- •Implementing the Complex Class
- •Creating the Conversion Code
- •Testing the Conversion Code
- •A Sample Program
- •Componentizing
- •Restructuring
- •Specialization
- •Index
32 Creating Templates
from Functions and
Technique Methods
Save Time By
Creating function templates
Creating method templates
Interpreting your output
Although creating entire classes that are templates is useful, sometimes you just want a single function or method to accept a template argument. For example, if you created a comparison function
that used a template for all types, you could then create (say) a minimum function that would compare any two data types, including classes, as long as you could tell one was of lesser magnitude than the other. This technique shows how to save time by templatizing only a single function (and later, a single method) of a class.
Implementing Function Templates
A function template, or method template, is simply a standalone function (inside a class, in the case of a method) that can accept one or more template arguments. Let’s take a look at how you would implement function templates in your own code, by creating a generic function that will compute the minimum of two values.
1.
2.
In the code editor of your choice, create a new file to hold the code for the implementation of the source file.
In this example, the file is named ch32.cpp, although you can use whatever you choose.
Type the code from Listing 32-1 into your file.
Better yet, copy the code from the source file on this book’s companion Web site.
Implementing Function Templates |
187 |
LISTING 32-1: THE MIN TEMPLATE FUNCTION
#include <stdio.h> #include <string>
template <class A>
A my_min( const A& val1, const A& val2)
{
if ( val1 == val2 ) return val1;
if ( val1 < val2 ) return val1;
return val2;
}
bool operator==(const std::string& s1, const std::string& s2)
{
int len = s1.length();
if ( len != s2.length() ) return false;
for ( int i=0; i<len; ++i ) if ( s1[i] != s2[i] )
return false;
return true;
}
bool operator <(const std::string& s1, const std::string& s2
{
int len = s1.length(); if ( len > s2.length() )
len = s2.length();
for ( int i=0; i<len; ++i ) if ( s1[i] > s2[i] )
return false;
return true;
}
int main(int argc, char **argv)
{
//First, try it for integers int x1 = 100;
int x2 = 30;
int xmin = my_min(x1, x2);
//Now, for floating-point numbers float f1 = 12.40;
float f2 = 4.90;
float fmin = my_min(f1, f2);
1
5
6
2
(continued)
188 Technique 32: Creating Templates from Functions and Methods
LISTING 32-1 (continued)
int xmin2 = my_min(x2, x1);
printf(“Xmin = %d\n”, xmin); printf(“Xmin2 = %d\n”, xmin2 ); printf(“Fmin = %f\n”, fmin );
//Now that we have implemented the operators,
//try it for strings.
std::string s1 = “Hello world”; std::string s2 = “Goodbye cruel world”;
if ( s1 == s2 )
printf(“Strings are equal\n”); else
if ( s1 < s2 )
printf(“string %s is less\n”, s1.c_str() ); else
printf(“String %s is less\n”, s2.c_str() );
std::string smin = my_min( s1, s2 );
printf(“Min for strings returned: %s\n”, smin.c_str() );
}
3
4
3. Save the source file in your code editor and close the code editor.
Note that we have created a templated function called my_min, shown at 1 (it was not called min, because that would conflict with a Standard Template Library (STL) function of the same name), which can be used with any data type that supports the equal (=) and less-than (<) operators.
In this case, the source code also implements less-than and equal-to operations for the standard string class in the STL. After we have implemented these operators, we can then instantiate the template for the string class.
4. Compile the source code with the compiler of your choice on the operating system of your choice.
When the program is run, if you have done everything properly, you should see the following output in the shell window:
$ ./a.exe Xmin = 30 Xmin2 = 30
Fmin = 4.900000
String Goodbye cruel world is less
Min for strings returned: Goodbye cruel world
Let’s look at this carefully, and see what is going on. We are calling the minimum function, my_min, in three locations, shown at the lines marked with 2,
3, and 4. The first line computes the min fortwo integers, the second for two floating point numbers, and the third for two strings.
Although integers and floating point numbers have their own comparison operators (less than, greater than, and so forth) built into the language, strings do not. Therefore, we implement the comparison functions that will be called by my_min at the lines marked 5 and 6. After all of this is done, the compiler generates the proper versions of these functions and you see the minimum calculation output shown above.
Creating Method Templates 189
In this example, the template is automatically generated. Unlike a template class, function templates don’t require the programmer to specify the class or type that the template is being implemented for. This saves a lot of time; you don’t have to track down where the template was instantiated. The linker is also smart enough, with most modern C++ compilers, to link only in one copy of a given template. This means that if you have multiple calls to a specific templated function in your program, only one will be in the final executable. If your linker is not that smart, you have to force the implementation of a given template in your code.
Creating Method Templates
Similarly, you can also create methods of classes that are themselves templates, even though the class as a whole might not be. You might want to do this as a way to avoid writing a lot of the same code over and over, such as creating a set of assignment methods for different data types.
When you find yourself writing the same method over and over, but specifying a different data type (such as a set method or an assignment operator) for each one, immediately think about creating a template method for that operation. This can save you a lot of time.
The following steps show you how to create a method template:
1. In the code editor of your choice, reopen the source file for the code that you just created.
In this example, the file is named ch32.cpp, although you can use whatever you choose.
2. Add the code from Listing 32-2 into your file.
Or better yet, copy the code from the source file on this book’s companion Web site.
LISTING 32-2: A CLASS WITH A TEMPLATED METHOD
class Foo
{
private:
std::string _name; int _value;
public:
Foo(void)
{
_name = “Nothing”; _value = 0;
}
Foo(const char *strName, int iValue)
{
_name = strName; _value = iValue;
}
Foo( const Foo& aCopy )
{
_name = aCopy._name; _value = aCopy._value;
}
Foo operator=( const Foo& aCopy )
{
_name = aCopy._name; _value = aCopy._value; return *this;
}
//Templatized method to add values template < class A >
void Add( const A& aValue )
{
_value += aValue;
}
//Templatized method to multiply values template < class A >
void Multiply( const A& aValue ) |
|
7 |
{ |
|
_value = _value * aValue;
}
// Method to dump the values void Print()
{
printf(“Name: [%s]\n”, _name.c_str()
);
printf(“Value: %d\n”, _value );
}
};
(continued)
190 Technique 32: Creating Templates from Functions and Methods
LISTING 32-2 (continued)
int operator*( int iValue, std::string s )
{
//Convert string to an integer int iMult = atoi( s.c_str() );
//Do the multiplication
int iResult = iValue * iMult; // Return it
return iResult;
}
The above listing shows a class, Foo, which contains a templated method called Multiply (shown at 7). This method will allow you to multiply various types of data and assign the result to a member variable within the class. Notice also the operator* function that is defined even to multiply our value by a string.
3. Change the main function of the source file to be as shown in Listing 32-3:
LISTING 32-3: THE TEST DRIVER FOR THE TEMPLATED METHOD
int main(int argc, char **argv)
{
//First, try it for integers int x1 = 100;
int x2 = 30;
int xmin = my_min(x1, x2);
//Now, for floating point numbers float f1 = 12.40;
float f2 = 4.90;
float fmin = my_min(f1, f2);
int xmin2 = my_min(x2, x1);
printf(“Xmin = %d\n”, xmin); printf(“Xmin2 = %d\n”, xmin2 ); printf(“Fmin = %f\n”, fmin );
//Now that we have implemented the oper
//ators, try it for strings. std::string s1 = “Hello world”; std::string s2 = “Goodbye cruel world”;
if ( s1 == s2 )
printf(“Strings are equal\n”); else
if ( s1 < s2 )
printf(“string %s is less\n”, s1.c_str() );
else
printf(“String %s is less\n”, s2.c_str() );
std::string smin = my_min( s1, s2 ); printf(“Min for strings returned: %s\n”, smin.c_str() );
Foo f(“MyFoo”, 10); f.Add( 12.0 ); f.Print(); f.Multiply( -1 ); f.Print();
f.Multiply(std::string(“12”));
f.Print();
}
Note that as with template functions, the programmer does not in any way create an instance of the templated member function. The compiler will create instances as needed, by matching up the possible template arguments with the available templates. Naturally, this will only work if the template class definition is available, so once again, the template methods must be in-line defined methods. Because there is no “natural” way in which to multiply a string by an integer, we define a global operator which accepts an integer value and a string, and returns the converted string multiplied by the integer. After this operator is defined, we can then pass in a string to the templated method. (If the operator was not defined, you get a compile error when the template is expanded and the integer multiplication by the input argument is attempted.) There is a quite a bit going on here, obviously.
4. Compile the source code with the compiler of your choice on the operating system of your choice.
Creating Method Templates 191
When the program is run, if you have done everything properly, you should see the following output in the shell window:
$ ./a.exe Xmin = 30 Xmin2 = 30
Fmin = 4.900000
String Goodbye cruel world is less
Min for strings returned: Goodbye cruel |
|
|
world |
|
8 |
Name: [MyFoo] |
|
|
Value: 22 |
|
Name: [MyFoo]
Value: -22
Name: [MyFoo]
Value: -264
The initial part of our output is from the first part of this technique and has not changed. The second part, beginning with the line marked 8, shows the result of our templated method. As you can see, we first assign the member variable value the value 10. We then add 12 to it, resulting in the output of 22. Now, we start using the Multiple method. First, we multiply by an integer value of –1, resulting in the output of –22. Then we multiply by a string value
of 12, which is converted to an integer using the operator* function we defined, which results in –264 — the value that is printed out.
Note that the string is evaluated to its integer value, 12, and the result multiplied by the current value of –22. This results in a total of –264, which is the value displayed on the console window.