//: C03:ICompare.cpp
#include <cassert>
#include <iostream>
#include "ichar_traits.h"
using namespace std;
int main() {
// The same letters except for case:
istring first = "tHis";
istring second = "ThIS";
cout << first << endl;
cout << second << endl;
assert(first.compare(second) == 0);
assert(first.find('h') == 1);
assert(first.find('I') == 2);
assert(first.find('x') == string::npos);
} ///:~
This is just a toy example, of course. To make istring fully equivalent to string, we’d have to create the other functions necessary to support the new istring type.
The <string> header provides a wide string class via the following typedef:
typedef basic_string<wchar_t> wstring;
Wide string support also reveals itself in wide streams (wostream in place of ostream, also defined in <iostream>) and in the header <cwctype>, a wide-character version of <cctype>. This along with the wchar_t specialization of char_traits in the standard library allows us to do a wide-character version of ichar_traits:
//: C03:iwchar_traits.h
//{-bor}
//{-g++}
// Creating your own wide-character traits
#ifndef IWCHAR_TRAITS_H
#define IWCHAR_TRAITS_H
#include <cassert>
#include <cwctype>
#include <cmath>
#include <ostream>
#include <string>
using std::towupper;
using std::towlower;
using std::wostream;
using std::wstring;
using std::char_traits;
using std::allocator;
using std::basic_string;
struct iwchar_traits : char_traits<wchar_t> {
// We'll only change character-by-
// character comparison functions
static bool eq(wchar_t c1st, wchar_t c2nd) {
return towupper(c1st) == towupper(c2nd);
}
static bool ne(wchar_t c1st, wchar_t c2nd) {
return towupper(c1st) != towupper(c2nd);
}
static bool lt(wchar_t c1st, wchar_t c2nd) {
return towupper(c1st) < towupper(c2nd);
}
static int compare(const wchar_t* str1,
const wchar_t* str2, size_t n) {
for(size_t i = 0; i < n; i++) {
if(str1 == 0)
return -1;
else if(str2 == 0)
return 1;
else if(towlower(*str1) < towlower(*str2))
return -1;
else if(towlower(*str1) > towlower(*str2))
return 1;
assert(towlower(*str1) == towlower(*str2));
str1++; str2++; // Compare the other wchar_ts
}
return 0;
}
static const wchar_t* find(const wchar_t* s1,
size_t n, wchar_t c) {
while(n-- > 0)
if(towupper(*s1) == towupper(c))
return s1;
else
++s1;
return 0;
}
};
typedef basic_string<wchar_t, iwchar_traits> iwstring;
inline wostream& operator<<(wostream& os,
const iwstring& s) {
return os << wstring(s.c_str(), s.length());
}
#endif // IWCHAR_TRAITS_H ///:~
As you can see, this is mostly an exercise in placing a ‘w’ in the appropriate place in the source code. The test program looks like this:
//: C03:IWCompare.cpp
//{-g++}
#include <cassert>
#include <iostream>
#include "iwchar_traits.h"
using namespace std;
int main() {
// The same letters except for case:
iwstring wfirst = L"tHis";
iwstring wsecond = L"ThIS";
wcout << wfirst << endl;
wcout << wsecond << endl;
assert(wfirst.compare(wsecond) == 0);
assert(wfirst.find('h') == 1);
assert(wfirst.find('I') == 2);
assert(wfirst.find('x') == wstring::npos);
} ///:~
Unfortunately, some compilers still do not provide robust support for wide characters.
A string application
If you’ve looked at the sample code in this book closely, you’ve noticed that certain tokens in the comments surround the code. These are used by a Python program that Bruce wrote to extract the code into files and set up makefiles for building the code. For example, a double-slash followed by a colon at the beginning of a line denotes the first line of a source file. The rest of the line contains information describing the file’s name and location and whether it should be only compiled rather than fully built into an executable file. For example, the first line in the previous program above contains the string C03:IWCompare.cpp, indicating that the file IWCompare.cpp should be extracted into the directory C03.
The last line of a source file contains a triple-slash followed by a colon and a tilde. If the first line has an exclamation point immediately after the colon, the first and last lines of the source code are not to be output to the file (this is for data-only files). (If you’re wondering why we’re avoiding showing you these tokens, it’s because we don’t want to break the code extractor when applied to the text of the book!).
Bruce’s Python program does a lot more than just extract code. If the token "{O}" follows the file name, its makefile entry will only be set up to compile the file and not to link it into an executable. (The Test Framework in Chapter 2 is built this way.) To link such a file with another source example, the target executable’s source file will contain an "{L}" directive, as in.
//{L} ../TestSuite/Test
This section will present a program to just extract all the code so that you can compile and inspect it manually. You can use this program to extract all the code in this book by saving the document file as a text file[36] (let’s call it TICV2.txt) and by executing something like the following on a shell command line:.
C:> extractCode TICV2.txt /TheCode
This command reads the text file TICV2.txt and writes all the source code files in subdirectories under the top-level directory /TheCode. The directory tree will look like the following:
TheCode/
C0B/
C01/
C02/
C03/
C04/
C05/
C06/
C07/
36
Beware that some versions of Microsoft Word erroneously replace single quote characters with an extended ASCII character when you save a document as text, which of course causes a compile error. We have no idea why this happens. Just replace the character manually with an apostrophe.