- •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
266 Technique 46: Creating an Internationalization Class
Only by doing all this can we save time when creating applications in multiple languages. If you write applications with any sort of regional or international appeal, eventually you must internationalize them. By building in this support up front — and giving the application the capability to load those literal strings from external sources — you not only save time later on, but also save huge amounts of space in your application memory. This approach also allows you to customize your error messages and display prompts directed to different age and regional groups. This is the procedure we will be using in this technique to illustrate how to save time and effort up front by creating a single way in which to internationalize your applications.
don’t want to store all possible languages in your application because that would cause the memory requirements to go through the roof. So we store our languages in compressed-text format by squeezing out the returns and spaces between the items of data. These steps show you 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 ch46.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-1 into your file.
Building the Language Files |
Better yet, copy the code from the source file on |
this book’s companion Web site. |
Before you can display text, you need to be able to build files that contain the language data. You
LISTING 46-1: THE STRINGENTRY CLASS
#include <stdio.h> #include <string> #include <vector> #include <iostream> #include <fstream>
using namespace std;
#define VERSION_STRING “Version 1.0.0”
class StringEntry
{
private:
unsigned long _id; string _strEntry; unsigned long _offset; unsigned long _length;
protected:
void Init()
{
setID ( 0 ); setString( “” );
Building the Language Files 267
setOffset( 0 ); setLength( 0 );
}
void Copy( const StringEntry& aCopy )
{
setID ( aCopy.ID() ); setString( aCopy.String() ); setOffset( aCopy.Offset() ); setLength ( aCopy.Length() );
}
public:
StringEntry(void)
{
Init();
}
StringEntry( unsigned long id, const char *strIn )
{
Init(); setID( id );
setString( strIn );
//For now, assign the length to just be the length
//of the string.
setLength( strlen(strIn) );
}
StringEntry( const StringEntry& aCopy )
{
Copy( aCopy );
}
StringEntry operator=( const StringEntry& aCopy )
{
Copy( aCopy );
}
unsigned long ID() const
{
return _id;
}
string String() const
{
return _strEntry;
}
unsigned long Offset() const
{
return _offset;
}
unsigned long Length() const
{
return _length;
}
(continued)
268 Technique 46: Creating an Internationalization Class
LISTING 46-1 (continued)
void setID( unsigned long id )
{
_id = id;
}
void setString( const char *strIn )
{
_strEntry = strIn;
}
void setString( const string& sIn )
{
_strEntry = sIn;
}
void setOffset( unsigned long offset )
{
_offset = offset;
}
void setLength( unsigned long length )
{
_length = length;
}
virtual void write( ofstream& out )
{
//Get the current output position. setOffset( out.tellp() );
//Write out the string.
const char *strOut = String().c_str(); out << strOut;
} |
|
|
|
virtual |
void dump( ostream& out ) |
||
{ |
|
|
|
out |
<< “StringEntry:” << endl; |
||
out |
<< “ID |
: “ << ID() << endl; |
|
out |
<< “String: [“ << String().c_str() << “]” << endl; |
||
out |
<< “Length: “ << Length() << endl; |
||
out |
<< “Offset: “ << Offset() << endl; |
||
} |
|
|
|
}; |
|
|
|
class StringWriter |
|
|
|
{ |
|
|
|
private: |
|
|
|
vector< |
StringEntry > |
_entries; |
|
string |
|
|
_fileName; |
string |
|
|
_outputFileName; |
Building the Language Files 269
string get_line( ifstream& in )
{
string |
sOut = “”; |
|
char |
cLastChar = 0; |
|
while ( !in.eof() |
) |
|
{ |
|
|
//Read in a character at a time. If we hit end of line,
//and the last character is NOT \, we are done.
char c; in.get(c);
if ( in.fail() ) break;
if ( c == ‘\n’ )
{
// We found a return. See whether the last thing was a backslash. if ( cLastChar != ‘\\’ )
break;
else
{
// Remove the backslash.
sOut = sOut.substr(0, sOut.length()-1);
}
}
sOut += c; cLastChar = c;
}
return sOut;
}
virtual bool ProcessLine( const string& sIn )
{
// There has to be a colon (:).
int nColonPos = sIn.find_first_of( ‘:’ ); if ( nColonPos == string::npos )
return false;
// Get the pieces.
string sNumber = sIn.substr(0, nColonPos); string sValue = sIn.substr( nColonPos+1 );
// Add it to our list.
StringEntry se( atol(sNumber.c_str()), sValue.c_str() ); _entries.insert( _entries.end(), se );
return false;
}
(continued)
270 Technique 46: Creating an Internationalization Class
LISTING 46-1 (continued)
virtual bool Load()
{
//Try to open the input file. ifstream in(_fileName.c_str()); if ( in.fail() )
return false;
//Read in the first line for version information. string sLine = get_line(in);
if ( strcmp( sLine.c_str(), VERSION_STRING ) ) return false;
for ( int i=0; i<10; ++i )
{
if ( in.fail() ) break;
sLine = get_line(in); // Ignore blank lines.
if ( sLine.length() == 0 ) continue;
// Ignore comments. if ( sLine[0] == ‘#’ )
continue;
if ( ProcessLine( sLine ) )
printf(“Invalid input: %s\n”, sLine.c_str () );
}
}
public:
StringWriter( void )
{
}
StringWriter( const char *inputFileName, const char *outputFileName )
{
_fileName = inputFileName; _outputFileName = outputFileName; Load();
}
virtual bool Save( void )
{
//If there are no entries, abort. if ( _entries.size() == 0 )
return false;
//Try to open the output file.
ofstream out( _outputFileName.c_str() ); if ( out.fail() )
return false;
1
2
Building the Language Files 271
// Okay, process each of them. vector< StringEntry >::iterator iter;
for ( iter = _entries.begin(); iter != _entries.end(); ++iter )
{
// Write out the entry. (*iter).write( out );
}
// Now, process the index file.
string indexFileName = _outputFileName + “.idx”; ofstream out2(indexFileName.c_str());
if ( out2.fail() )
{
printf(“Unable to open index file %s for output\n”, indexFileName.c_str() ); return false;
}
for ( iter = _entries.begin(); iter != _entries.end(); ++iter )
{
// Write out the entry.
out2 << (*iter).Offset() << “, “ << (*iter).Length() << “, “ << (*iter).ID() << endl;
}
return true;
}
};
int main(int argc, char **argv)
{
if ( argc < 3 )
{
printf(“Usage: StringEntry input-file output-file\n” );
printf(“Where: input-file is the file containing the string definitions\n”); printf(“ output-file is the final generated file name\n”);
return(-1);
}
StringWriter s(argv[1], argv[2]); if ( s.Save() == false )
printf(“Error generating file\n”);
return 0;
}
The code above breaks down into a storage class (StringEntry), a writing class (StringWriter), and a test driver that illustrates how to use the code. The test driver expects two arguments: a file that contains the string definitions and an argument that specifies the name of the file to create for an output file. The input file simply
consists of ID numbers (integer values) followed by a colon and then the string to encode into the output file. Each entry in the definition file is read, parsed, and placed into a StringEntry object. This all happens in the Load function
of the StringWriter class shown at 1. After the entire input file is parsed, it is written to the