- •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
Creating the Wildcard Matching Class |
331 |
matches both AB and AbB. So the expression con?cious matches the word conscious whether or not it included an s in that position.
Often users want to be able to use wildcards to filter data. If you give them this capability, you can save yourself a lot of time in supporting them. Appropriately used, wildcards can help make life a bit easier for everyone.
The question-mark and asterisk characters are common wildcards — but not the only ones. In the SQL language, for example, you use a percent sign (%) instead of an asterisk to match multiple characters. For this reason, when you design a class that permits wildcards in search strings, you should allow that information to be configurable. The purpose of this technique is to show you how to create a class that performs matching with wildcards. This class can be used to quickly and easily add pattern matching functionality to your application, which saves you time and effort in developing quality software that users really want.
Creating the Wildcard
Matching Class
In order to best utilize wildcard matching in your application, you should encapsulate the functionality for matching strings into a single class. That class will handle both the jobs of storing the match characters (such as an asterisk or question mark) and determining if the two strings match. Let’s develop such a class and a test driver to illustrate how it is used. Here’s how:
1. In the code editor of your choice, create a new file to hold the code for the source file of the technique.
In this example, the file is named ch55.cpp, although you can use whatever you choose.
2. Type the code from Listing 55-1 into your file.
Better yet, copy the code from the source file on this book’s companion Web site.
LISTING 55-1: THE MATCH CLASS
#include <iostream> #include <string>
using namespace std;
class Match
{
private:
char _MatchMultiple; char _MatchSingle; string _pattern; string _candidate;
protected:
bool match(const char *pat, const char *str)
{
if ( *pat == ‘\0’ ) return !*str;
else
if ( *pat == _MatchMultiple )
return match(pat+1, str) || (*str && match(pat, str+1));
(continued)
332 Technique 55: Using Wildcards
LISTING 55-1 (continued)
else
if ( *pat == _MatchSingle )
return *str && (match(pat+1, str+1) || match(pat+1, str)); return (*str == *pat) && match(pat+1, str+1);
}
public:
Match(void)
{
_MatchMultiple = ‘*’; _MatchSingle = ‘?’;
}
Match( const char *pat, const char *str )
{
_MatchMultiple = ‘*’; _MatchSingle = ‘?’; _pattern = pat; _candidate = str;
}
Match( const Match& aCopy )
{
_MatchMultiple = aCopy._MatchMultiple;
_MatchSingle |
= aCopy._MatchSingle; |
|
_pattern |
= |
aCopy._pattern; |
_candidate |
= |
aCopy._candidate; |
}
Match operator=( const Match& aCopy )
{
_MatchMultiple = aCopy._MatchMultiple;
_MatchSingle |
= aCopy._MatchSingle; |
|
_pattern |
= |
aCopy._pattern; |
_candidate |
= |
aCopy._candidate; |
return *this; |
|
|
}
char Multiple(void)
{
return _MatchMultiple;
}
char Single(void)
{
return _MatchSingle;
}
void setMultiple( char mult )
{
_MatchMultiple = mult;
}
void setSingle( char single )
{
_MatchSingle = single;
}
Testing the Wildcard Matching Class |
333 |
void setPattern( const char *pattern )
{
_pattern = pattern;
}
void setCandidate( const char *candidate )
{
_candidate = candidate;
}
string getPattern( void )
{
return _pattern;
}
string getCandidate( void )
{
return _candidate;
}
bool matches()
{
return match( _pattern.c_str(), _candidate.c_str() );
}
};
The purpose of this class is to see whether or not two strings match, including wildcards if necessary. To accomplish this, we need the following:
A multiple character wildcard
A single character wildcard
An input pattern string
The candidate match string
For example, if we wanted to allow the user to match the string Colour as well as Color so that we could check for British spellings, we would use the following:
Multiple character wildcard: An asterisk (*)
Single character wildcard: A question mark (?)
Input match string: Colo*r
Candidate match string: Either Color or
Colour
The result of this should be a positive match. To do this, we built a class that contained member variables for the match characters and strings, and routines to access those match elements. In
addition, the class contains a single method, called matches, which indicates if the input and candidate strings match.
3. Save the source code in the code editor.
Testing the Wildcard
Matching Class
After you create a class, you should create a test driver that not only ensures that your code is correct, but also shows people how to use your code.
The following steps show you how to create a test driver to illustrate various kinds of input from the user, and show how the class is intended to be used.
1. In the code editor of your choice, reopen the source file to hold the code for your test program.
In this example, I named the test program ch6_12.cpp.
334 Technique 55: Using Wildcards
2. Type the code from Listing 55-2 into your file.
Better yet, copy the code from the source file on this book’s companion Web site.
LISTING 55-2: THE WILDCARD MATCHING TEST DRIVER
string get_a_line( istream& in )
{
string retStr;
while ( !in.fail() )
{
char c; in.get(c);
if ( in.fail() ) break;
if ( c != ‘\r’ && c != ‘\n’ ) retStr += c;
if ( c == ‘\n’ ) break;
}
return retStr;
}
int main(int argc, char **argv)
{
char szPattern[ 80 ]; char szString [ 80 ]; bool done = false;
while ( !done )
{
cout << “Enter the pattern: “; string sPattern = get_a_line( cin ); if ( !sPattern.length() )
done = true;
else
{
cout << “Enter the string: “; string sString = get_a_line( cin
);
Match m(sPattern.c_str(), sString.c_str() );
if ( m.matches() ) printf(“match\n”);
else
printf(“no match\n”);
}
}
}
The test driver simply gets two strings from the user and uses wildcard matching to see if they match. The pattern string may contain optional wildcards, although the string to match may not. By utilizing the Match class that we developed in Listing 55-1, we check to see if the two strings are wildcard matches of each other.
3. Save the source code in the editor and close the editor application.
4. Compile the application, using your favorite compiler on your favorite operating system.
5. Run the application on your favorite operating system.
If you have done everything right, you should see the following session on your console window:
$ ./a.exe
Enter the pattern: A*B Enter the string: AB match
Enter the pattern: A*B Enter the string: AajkjB match
Enter the pattern: A*B Enter the string: ABC no match
Enter the pattern: A?B Enter the string: AbaB no match
Enter the pattern:
As you can see, the matching class works as advertised.
Part VIII
Utilities
56 |
Encoding and |
|
|
|
Decoding Data |
Technique |
for the Web |
|
Save Time By
Interfacing with the Internet
Encoding and decoding URLs for use on the Internet
Creating a URL Codec class
Testing that class
The World Wide Web has brought with it a host of new opportunities and a host of new problems. Most applications these days need to be Web-enabled to work directly with Web browsers or Web appli-
cations. No matter what kind of application you’re developing, odds are that the application will have to interact with the Web or with remote systems that use Web protocols.
The biggest issue in interfacing with the Internet is that of encoding. Encoding is the process of translating characters that cannot be directly used by a system into characters that can. For the World Wide Web, for example, characters such as the ampersand (&), greaterand lessthan signs (> and <), and others cannot be directly used. We need to change them into a form that the Web can use. The Web identifies addresses with a Uniform Resource Locator, better known as a URL. One of the rules of working with URLs is that they cannot contain characters such as spaces and slashes, because including them would break many existing browser applications and operating systems. Browsers assume that spaces and slashes indicate breaks in a URL, which is the standard format for Web addresses. There is no way to change the browser, so we must change the string.
The problem is that the C++ library offers no standard way to encode and decode URL strings. The technique for encoding and decoding is well known, but it is new enough that it has not yet made it into the STL or standard C++ library. For this reason, we end up reimplementing the code in each and every application that we write that needs the functionality. This is contrary to the C++ principle of “write once, reuse many times.”
Saving time is often about anticipating the needs of your application and planning for them in advance. By planning to Web-enable your code — regardless of whether you expect your application to support the Web (initially, at least) — you save a lot of time in the long-run. It makes sense, then, to create a single, reusable class that will do the encoding and decoding work, one you can insert as needed in the applications you develop. That’s what this technique is all about.