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

272 Technique 46: Creating an Internationalization Class
output format in the Save method of the StringWriter class, shown at 2. The result of running this program should be a language file that can then be used by your application for international uses.
3. Save the source code in your code editor.
Creating an Input Text File
After we have created the application to read the text file and convert it into an international language file, the next step is to test the application by creating a simple text file that contains strings we will use in the final international language file. The following steps show you how to do that by creating a very small text file we can use for testing the application:
1. In the code editor of your choice, create a new file to hold the text for the test language file we will be using.
In this example, the file is named test.in.eng, although you can use whatever name you choose. This file will contain the strings we wish to place in the output international language file.
2. Type the following text into your file, substituting your own values wherever you choose.
Better yet, copy the code from the source file on this book’s companion Web site.
Version 1.0.0
#This is a comment
#This is another comment \ but it is very very long
1:Hello |
|
3 |
2:Goodbye |
|
3:Why me? 4:English 5:French
3. Compile the source file with your favorite compiler, on your favorite operating system.
In this example, the source file was called StringEntry.cpp; however, you may call it anything you like.
4. Run the application on the operating system of your choice, using the input file as an argument to the application.
If you have done everything correctly, you will see the following output in the console window, and will have two files created in the file system (called my.eng and my.eng.idx):
$ ./a.exe test.in.eng my.eng |
|
|
|
The my.eng file will look like this: |
|
|
|
$ cat my.eng |
4 |
||
HelloGoodbyeWhy me?EnglishFrench |
|||
The my.eng.idx file will look like this: |
|
||
$ cat my.eng.idx |
|
5 |
|
0, 5 |
|
|
|
5, 7 |
|
|
|
12, |
7 |
|
|
19, |
7 |
|
|
26, |
6 |
|
|
As you can see from the two outputs shown above, after the writer is finished with the input text file, it is no longer truly in readable format. The strings are concatenated in a binary output format, with a secondary file containing indices that indicate where each string starts and ends. For example, the input
file contains the string shown in the listing at |
|
3 |
|
This string is then written to the binary |
output file, |
||
|
|
|
my.eng, at 4 The index for this particular entry
in the |
|
|
5 |
The first |
|
is shown |
my.eng.idx file at |
|
|
||
entry in the index indicates the |
position in the file |
||||
|
|
|
|
(0-based). As you can see, the string we are looking at begins at the first position of the output file. The second entry in the index indicates the length of the string, in this case five. So, we go to position zero, count off five characters and that will be our first string. And, as you can see, that is in fact the string
Hello.
Reading the International File
After we have created the file and the index file for it, the next step is to build a reader that reads the strings

Reading the International File 273
in the various languages. The reader uses a two-step process to achieve this: First it reads in the index file so that it knows where all the strings are in the file, and then it loads a string to be read. The following steps show you how to do this.
Strings take up a large amount of the memory of an application and provide clues for hackers. For example, error messages may indicate where the processing for security is handled in the code. By finding these strings in the program executable file, the hacker can then determine where to make patches to “crack” your program to not require a license. When extracting all of the text for a system into external files, you should either encrypt the text to make it secure, or at least have it removed from the portion of the program that
it describes. This will save you a lot of time in securing — as well as debugging — your application, by eliminating at least one type of problem from the released product.
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 ch46_1.cpp, although you can use whatever you choose. This file will contain the class definition for our automation object.
2. Type the code from Listing 46-2 into your file.
Better yet, copy the code from the source file on this book’s companion Web site.
LISTING 46-2: THE STRINGREADER CLASS
#include <stdio.h> #include <vector> #include <string> #include <iostream> #include <fstream> #include <sstream>
using namespace std;
class StringIndex
{
private:
unsigned long _offset; unsigned long _length; unsigned long _id;
protected:
virtual void Init()
{
setOffset(0);
setLength(0);
setID(0);
}
public:
StringIndex(void)
{
Init();
}
StringIndex( unsigned long offset, unsigned long length, unsigned id )
(continued)

274 Technique 46: Creating an Internationalization Class
LISTING 46-2 (continued)
{
Init();
setOffset( offset ); setLength( length ); setID( id );
}
StringIndex( const StringIndex& aCopy )
{
setOffset( aCopy.getOffset() ); setLength( aCopy.getLength() ); setID ( aCopy.getID() );
}
virtual ~StringIndex()
{
}
StringIndex operator=( const StringIndex& aCopy )
{
setOffset( aCopy.getOffset() ); setLength( aCopy.getLength() ); setID ( aCopy.getID() );
return *this;
}
void setOffset( unsigned long offset )
{
_offset = offset;
}
void setLength( unsigned long length )
{
_length = length;
}
void setID( unsigned long id )
{
_id = id;
}
unsigned long getOffset( void ) const
{
return _offset;
}
unsigned long getLength( void ) const
{
return _length;
}
unsigned long getID( void ) const
{
return _id;
}

Reading the International File 275
virtual void dump( void )
{
cout << |
“StringIndex: |
“ << endl; |
||
cout << |
“Offset: “ << |
getOffset() << endl; |
||
cout << |
“Length: “ |
<< |
getLength() << endl; |
|
cout << |
“ID |
: “ |
<< |
getID() << endl; |
}
};
class StringReader
{
private:
string _fileName;
vector< StringIndex > _indices; protected:
virtual bool Load(void)
{
string indexFileName = _fileName + “.idx”; ifstream in(indexFileName.c_str());
if ( in.fail() ) return false;
// Read in each line. while ( !in.eof() )
{
string sIn = “”;
while ( !in.eof() )
{
// Get an input line. char c;
in.get(c);
if ( in.fail() ) break;
if ( c != ‘\r’ && c != ‘\n’ ) sIn += c;
if ( c == ‘\n’ ) break;
}
if ( sIn.length() == 0 ) break;
// Okay, we have a line. Now, parse it. istringstream iss(sIn.c_str());
(continued)

276 Technique 46: Creating an Internationalization Class
LISTING 46-2 (continued)
char c;
long lLength = 0; long lOffset = 0; long lID = 0;
// Parse the line, eating the comma
iss >> lOffset >> c >> lLength >> c >> lID;
StringIndex si(lOffset, lLength, |
lID); |
|
6 |
si.dump(); |
|
|
|
_indices.insert( _indices.end(), |
si ); |
|
}
return true;
}
virtual string _loadString( const StringIndex& si )
{
ifstream in(_fileName.c_str()); in.seekg( si.getOffset() ); string retStr;
for ( int i=0; i<si.getLength(); ++i )
{
char c; in.get(c);
if ( in.fail() ) break;
retStr += c;
}
return retStr;
}
public:
StringReader(void)
{
}
StringReader( const char *fileName )
{
_fileName = fileName; Load();
}
StringReader( const StringReader& aCopy )
{
_fileName = aCopy._fileName;
vector< StringIndex >::const_iterator iter;
for (iter = aCopy._indices.begin(); iter != aCopy._indices.end(); ++iter ) _indices.insert( _indices.end(), (*iter) );
}
string getString( long id )