//: C04:Effector.cpp
// Jerry Schwarz's "effectors"
#include
#include // For max()
#include
#include
using namespace std;
// Put out a prefix of a string:
class Fixw {
string str;
public:
Fixw(const string& s, int width)
: str(s, 0, width) {}
friend ostream&
operator<<(ostream& os, const Fixw& fw) {
return os << fw.str;
}
};
// Print a number in binary:
typedef unsigned long ulong;
class Bin {
ulong n;
public:
Bin(ulong nn) { n = nn; }
friend ostream& operator<<(ostream& os, const Bin& b) {
const ulong ULMAX = numeric_limits::max();
ulong bit = ~(ULMAX >> 1); // Top bit set
while(bit) {
os << (b.n & bit ? '1' : '0');
bit >>= 1;
}
return os;
}
};
int main() {
string words =
"Things that make us happy, make us wise";
for(int i = words.size(); --i >= 0;) {
ostringstream s;
s << Fixw(words, i);
assert(s.str() == words.substr(0, i));
}
ostringstream xs, ys;
xs << Bin(0xCAFEBABEUL);
assert(xs.str() ==
"1100""1010""1111""1110""1011""1010""1011""1110");
ys << Bin(0x76543210UL);
assert(ys.str() ==
"0111""0110""0101""0100""0011""0010""0001""0000");
} ///:~
The constructor for Fixwcreates a shortened copy of its char*argument, and the destructor releases the memory created for this copy. The overloaded operator<<takes the contents of its second argument, the Fixwobject, inserts it into the first argument, the ostream, and then returns the ostreamso that it can be used in a chained expression. When you use Fixwin an expression like this: .
cout << Fixw(string, i) << endl;
a temporary object is created by the call to the Fixwconstructor, and that temporary object is passed to operator<<. The effect is that of a manipulator with arguments. The temporary Fixwobject persists until the end of the statement .
The Bineffector relies on the fact that shifting an unsigned number to the right shifts zeros into the high bits. We use numeric_limits::max( )(the largest unsigned longvalue, from the standard header ) to produce a value with the high bit set, and this value is moved across the number in question (by shifting it to the right), masking each bit in turn. We’ve juxtaposed string literals in the code for readability; the separate strings are of course concatenated into one by the compiler .
Historically, the problem with this technique was that once you created a class called Fixwfor char*or Binfor unsigned long, no one else could create a different Fixwor Binclass for their type. However, with namespaces, this problem is eliminated .
In this section you’ll see some examples of what you can do with all the information you’ve learned in this chapter. Although many tools exist to manipulate bytes (stream editors such as sedand awkfrom UNIX are perhaps the most well known, but a text editor also fits this category), they generally have some limitations. Both sedand awkcan be slow and can only handle lines in a forward sequence, and text editors usually require human interaction, or at least learning a proprietary macro language. The programs you write with iostreams have none of these limitations: they’re fast, portable, and flexible .
Maintaining class library source code
Generally, when you create a class, you think in library terms: you make a header file Name.hfor the class declaration, and you create a file in which the member functions are implemented, called Name.cpp. These files have certain requirements: a particular coding standard (the program shown here uses the coding format for this book), and in the header file the declarations are generally surrounded by some preprocessor statements to prevent multiple declarations of classes. (Multiple declarations confuse the compiler—it doesn’t know which one you want to use. They could be different, so it throws up its hands and gives an error message.) .
This example allows you to create a new header/implementation pair of files or to modify an existing pair. If the files already exist, it checks and potentially modifies the files, but if they don’t exist, it creates them using the proper format .
//: C04:Cppcheck.cpp
// Configures .h & .cpp files to conform to style
// standard. Tests existing files for conformance.
#include
#include
#include
#include "../require.h"
using namespace std;
bool startsWith(const string& base, const string& key) {
return base.compare(0, key.size(), key) == 0;
}
void cppCheck(string fileName) {
enum bufs { BASE, HEADER, IMPLEMENT,
HLINE1, GUARD1, GUARD2, GUARD3,
CPPLINE1, INCLUDE, BUFNUM };
string part[BUFNUM];
part[BASE] = fileName;
// Find any '.' in the string:
size_t loc = part[BASE].find('.');
if(loc != string::npos)
part[BASE].erase(loc); // Strip extension
// Force to upper case:
for(size_t i = 0; i < part[BASE].size(); i++)
part[BASE][i] = toupper(part[BASE][i]);
// Create file names and internal lines:
part[HEADER] = part[BASE] + ".h";
part[IMPLEMENT] = part[BASE] + ".cpp";
part[HLINE1] = "//" ": " + part[HEADER];
part[GUARD1] = "#ifndef " + part[BASE] + "_H";
part[GUARD2] = "#define " + part[BASE] + "_H";
part[GUARD3] = "#endif // " + part[BASE] +"_H";
part[CPPLINE1] = string("//") + ": "
+ part[IMPLEMENT];
part[INCLUDE] = "#include \"" + part[HEADER] + "\"";
// First, try to open existing files:
ifstream existh(part[HEADER].c_str()),
existcpp(part[IMPLEMENT].c_str());
if(!existh) { // Doesn't exist; create it
ofstream newheader(part[HEADER].c_str());
assure(newheader, part[HEADER].c_str());
newheader << part[HLINE1] << endl
<< part[GUARD1] << endl
<< part[GUARD2] << endl << endl
<< part[GUARD3] << endl;
} else { // Already exists; verify it
stringstream hfile; // Write & read
ostringstream newheader; // Write
hfile << existh.rdbuf();
// Check that first three lines conform:
bool changed = false;
string s;
hfile.seekg(0);
getline(hfile, s);
bool lineUsed = false;
// The call to good() is for Microsoft (later too)
for (int line = HLINE1; hfile.good() && line <= GUARD2;
++line) {
if(startsWith(s, part[line])) {
newheader << s << endl;
lineUsed = true;
if (getline(hfile, s))
lineUsed = false;
} else {
newheader << part[line] << endl;
changed = true;
lineUsed = false;
}
}
// Copy rest of file
if (!lineUsed)
newheader << s << endl;
newheader << hfile.rdbuf();
// Check for GUARD3
string head = hfile.str();
if(head.find(part[GUARD3]) == string::npos) {
newheader << part[GUARD3] << endl;
changed = true;
}
// If there were changes, overwrite file:
if(changed) {
existh.close();
ofstream newH(part[HEADER].c_str());
assure(newH, part[HEADER].c_str());
Читать дальше