
- •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

260 Technique 45: Creating a Configuration File
Setting Up Your Test File
After you create any 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 that illustrates 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 ch45.cpp.
2. Type the code from Listing 45-3 into your file.
Better yet, copy the code from the source file on this book’s companion Web site.
LISTING 45-3: THE CONFIGURATION-FILE TEST CODE
#include <stdio.h>
#include “ConfigurationFile.h”
int main( int argc, char **argv )
{
if ( argc < 3 )
{
printf(“Usage: ch5_7 config-file- name arg1 [arg2 .. ]\n”);
printf(“Where: config-file- name is the name of the configuration file\n”);
printf(“ arg1 .. argn are the values to print out\n”);
return -1;
}
ConfigurationFile cf(argv[1]); if ( cf.read() == false )
{
printf(“Unable to read configuration file\n”);
return -2;
}
for ( int i=2; i<argc; ++i )
{
if ( !cf.hasValue( argv[i] ) )
{
printf(“Value %s NOT found in configuration file\n”, argv[i] );
}
else
{
string s = cf.getValue ( argv[i] );
printf(“Key %s = [%s]\n”, argv[i], s.c_str() );
}
}
return 0;
}
3. Save the source-code file in the code editor.
4. In the code editor of your choice, create a new text file to hold the actual configuration test file for your test program.
In this example, I named the test input data file input.cfg.
5. Type the following text into your file:
Color=Blue |
|
|
5 |
Name=Matt |
|
6 |
|
Address=” |
“ |
|
7 |
City=”Denver, |
CO” |
|
|
ZipCode=80232 |
|
|
#This is a comment
Testing the Configuration-File
Class
Now that everything’s set up, the following steps show you how to put it all together and go for a test drive:

Testing the Configuration-File Class |
261 |
1. Compile and run the source-code file (which we called ch45_7.cpp) along with the configuration-class file (which we called
ConfigurationFile.cpp) in your favorite compiler, on your favorite operating system.
2. Run the program.
If you have done everything right, you should see the following output in your console window:
$ ./a.exe input.cfg Color Name City State |
|
Key Color = [Blue] |
|
Key Name = [Matt] |
|
Key City = [Denver, CO] |
|
Value State NOT found in configuration |
8 |
file |
Looking at the input file, you can see that the values of Color, Name, and City are all entries (on the lefthand side of the equal sign). For those keys, the values are Blue, Matt, and Denver, CO. These items are
shown at the lines marked with |
5, 6 and |
|
7. |
configuration file |
|||
The test driver simply reads the |
|
|
|
using our configuration-file class and then displays the various key and value pairs. The test driver then exercises the full functionality of the retrieval code by looking for a value (State) that is not in the configuration file, and the code properly displays an
error as shown by the line marked with |
|
8. From |
you can see |
||
this output, and the test driver code, |
|
|
exactly how the configuration-file class was meant to be used, making it excellent documentation for the developer. You can also see from the listing that the output is what we were expecting, which makes it a good unit test. All in all, it shows just how you can save time and effort by using a standardized configuration format and using this class to read it.


Part VII
Using the Built-In
Functionality


46 Creating an
Internationalization
Technique Class
Save Time By
Understanding internationalization
Building language files
Reading an international file
Creating a string reader
Testing your code
Once upon a time, if you were programming in the United States, you tailored your applications only for English-speaking Americans. Your main concern was the code that implemented the
algorithms that were in your application; if you had an error message to display, you’d write a message that vaguely expressed the error and the user would just have to deal with it — no matter what language he spoke or how confusing the error message was. Fortunately, those days of usability-challenged code are over. Experts now create messages and indicators for applications so users can best understand what is going on. The error messages themselves are usually tailored to specific customer bases. Most importantly, however, our code is no longer directed only towards English-speaking Americans. Applications are distributed around the world, and need to work in any language, with any alphabet set. The capability to display messages in any language is known as internationalization.
You can’t simply bolt an internationalization feature onto your program — you have to design that capability in from the beginning. You can’t just translate messages on the fly, either; you have to know up front what the message is going to say. For this reason, creating a system that supports internationalization is important.
The process of internationalization is really threefold:
1. Identify all of the text that needs to be displayed in the various languages. Place this text into a single file, along with identifiers that can be used within the application to display the text.
2. Convert the text into a format that can be shipped easily with the application.
3. Provide a method to access all this content.