- •Thinking in C++ 2nd edition Volume 2: Standard Libraries & Advanced Topics
- •Preface
- •What’s new in the second edition
- •What’s in Volume 2 of this book
- •How to get Volume 2
- •Prerequisites
- •Learning C++
- •Goals
- •Chapters
- •Exercises
- •Exercise solutions
- •Source code
- •Language standards
- •Language support
- •The book’s CD ROM
- •Seminars, CD Roms & consulting
- •Errors
- •Acknowledgements
- •Library overview
- •1: Strings
- •What’s in a string
- •Creating and initializing C++ strings
- •Initialization limitations
- •Operating on strings
- •Appending, inserting and concatenating strings
- •Replacing string characters
- •Concatenation using non-member overloaded operators
- •Searching in strings
- •Finding in reverse
- •Finding first/last of a set
- •Removing characters from strings
- •Stripping HTML tags
- •Comparing strings
- •Using iterators
- •Iterating in reverse
- •Strings and character traits
- •A string application
- •Summary
- •Exercises
- •2: Iostreams
- •Why iostreams?
- •True wrapping
- •Iostreams to the rescue
- •Sneak preview of operator overloading
- •Inserters and extractors
- •Manipulators
- •Common usage
- •Line-oriented input
- •Overloaded versions of get( )
- •Reading raw bytes
- •Error handling
- •File iostreams
- •Open modes
- •Iostream buffering
- •Seeking in iostreams
- •Creating read/write files
- •User-allocated storage
- •Output strstreams
- •Automatic storage allocation
- •Proving movement
- •A better way
- •Output stream formatting
- •Internal formatting data
- •Format fields
- •Width, fill and precision
- •An exhaustive example
- •Formatting manipulators
- •Manipulators with arguments
- •Creating manipulators
- •Effectors
- •Iostream examples
- •Code generation
- •Maintaining class library source
- •Detecting compiler errors
- •A simple datalogger
- •Generating test data
- •Verifying & viewing the data
- •Counting editor
- •Breaking up big files
- •Summary
- •Exercises
- •3: Templates in depth
- •Nontype template arguments
- •Typedefing a typename
- •Using typename instead of class
- •Function templates
- •A string conversion system
- •A memory allocation system
- •Type induction in function templates
- •Taking the address of a generated function template
- •Local classes in templates
- •Applying a function to an STL sequence
- •Template-templates
- •Member function templates
- •Why virtual member template functions are disallowed
- •Nested template classes
- •Template specializations
- •A practical example
- •Pointer specialization
- •Partial ordering of function templates
- •Design & efficiency
- •Preventing template bloat
- •Explicit instantiation
- •Explicit specification of template functions
- •Controlling template instantiation
- •Template programming idioms
- •Summary
- •Containers and iterators
- •STL reference documentation
- •The Standard Template Library
- •The basic concepts
- •Containers of strings
- •Inheriting from STL containers
- •A plethora of iterators
- •Iterators in reversible containers
- •Iterator categories
- •Input: read-only, one pass
- •Output: write-only, one pass
- •Forward: multiple read/write
- •Bidirectional: operator--
- •Random-access: like a pointer
- •Is this really important?
- •Predefined iterators
- •IO stream iterators
- •Manipulating raw storage
- •Basic sequences: vector, list & deque
- •Basic sequence operations
- •vector
- •Cost of overflowing allocated storage
- •Inserting and erasing elements
- •deque
- •Converting between sequences
- •Cost of overflowing allocated storage
- •Checked random-access
- •list
- •Special list operations
- •list vs. set
- •Swapping all basic sequences
- •Robustness of lists
- •Performance comparison
- •A completely reusable tokenizer
- •stack
- •queue
- •Priority queues
- •Holding bits
- •bitset<n>
- •vector<bool>
- •Associative containers
- •Generators and fillers for associative containers
- •The magic of maps
- •A command-line argument tool
- •Multimaps and duplicate keys
- •Multisets
- •Combining STL containers
- •Creating your own containers
- •Summary
- •Exercises
- •5: STL Algorithms
- •Function objects
- •Classification of function objects
- •Automatic creation of function objects
- •Binders
- •Function pointer adapters
- •SGI extensions
- •A catalog of STL algorithms
- •Support tools for example creation
- •Filling & generating
- •Example
- •Counting
- •Example
- •Manipulating sequences
- •Example
- •Searching & replacing
- •Example
- •Comparing ranges
- •Example
- •Removing elements
- •Example
- •Sorting and operations on sorted ranges
- •Sorting
- •Example
- •Locating elements in sorted ranges
- •Example
- •Merging sorted ranges
- •Example
- •Set operations on sorted ranges
- •Example
- •Heap operations
- •Applying an operation to each element in a range
- •Examples
- •Numeric algorithms
- •Example
- •General utilities
- •Creating your own STL-style algorithms
- •Summary
- •Exercises
- •Perspective
- •Duplicate subobjects
- •Ambiguous upcasting
- •virtual base classes
- •The "most derived" class and virtual base initialization
- •"Tying off" virtual bases with a default constructor
- •Overhead
- •Upcasting
- •Persistence
- •MI-based persistence
- •Improved persistence
- •Avoiding MI
- •Mixin types
- •Repairing an interface
- •Summary
- •Exercises
- •7: Exception handling
- •Error handling in C
- •Throwing an exception
- •Catching an exception
- •The try block
- •Exception handlers
- •Termination vs. resumption
- •The exception specification
- •Better exception specifications?
- •Catching any exception
- •Rethrowing an exception
- •Uncaught exceptions
- •Function-level try blocks
- •Cleaning up
- •Constructors
- •Making everything an object
- •Exception matching
- •Standard exceptions
- •Programming with exceptions
- •When to avoid exceptions
- •Not for asynchronous events
- •Not for ordinary error conditions
- •Not for flow-of-control
- •You’re not forced to use exceptions
- •New exceptions, old code
- •Typical uses of exceptions
- •Always use exception specifications
- •Start with standard exceptions
- •Nest your own exceptions
- •Use exception hierarchies
- •Multiple inheritance
- •Catch by reference, not by value
- •Throw exceptions in constructors
- •Don’t cause exceptions in destructors
- •Avoid naked pointers
- •Overhead
- •Summary
- •Exercises
- •8: Run-time type identification
- •The “Shape” example
- •What is RTTI?
- •Two syntaxes for RTTI
- •Syntax specifics
- •Producing the proper type name
- •Nonpolymorphic types
- •Casting to intermediate levels
- •void pointers
- •Using RTTI with templates
- •References
- •Exceptions
- •Multiple inheritance
- •Sensible uses for RTTI
- •Revisiting the trash recycler
- •Mechanism & overhead of RTTI
- •Creating your own RTTI
- •Explicit cast syntax
- •Summary
- •Exercises
- •9: Building stable systems
- •Shared objects & reference counting
- •Reference-counted class hierarchies
- •Finding memory leaks
- •An extended canonical form
- •Exercises
- •10: Design patterns
- •The pattern concept
- •The singleton
- •Variations on singleton
- •Classifying patterns
- •Features, idioms, patterns
- •Basic complexity hiding
- •Factories: encapsulating object creation
- •Polymorphic factories
- •Abstract factories
- •Virtual constructors
- •Destructor operation
- •Callbacks
- •Observer
- •The “interface” idiom
- •The “inner class” idiom
- •The observer example
- •Multiple dispatching
- •Visitor, a type of multiple dispatching
- •Efficiency
- •Flyweight
- •The composite
- •Evolving a design: the trash recycler
- •Improving the design
- •“Make more objects”
- •A pattern for prototyping creation
- •Trash subclasses
- •Parsing Trash from an external file
- •Recycling with prototyping
- •Abstracting usage
- •Applying double dispatching
- •Implementing the double dispatch
- •Applying the visitor pattern
- •More coupling?
- •RTTI considered harmful?
- •Summary
- •Exercises
- •11: Tools & topics
- •The code extractor
- •Debugging
- •Trace macros
- •Trace file
- •Abstract base class for debugging
- •Tracking new/delete & malloc/free
- •CGI programming in C++
- •Encoding data for CGI
- •The CGI parser
- •Testing the CGI parser
- •Using POST
- •Handling mailing lists
- •Maintaining your list
- •Mailing to your list
- •A general information-extraction CGI program
- •Parsing the data files
- •Summary
- •Exercises
- •General C++
- •My own list of books
- •Depth & dark corners
- •Design Patterns
- •Index
The first command-line argument is the list of email addresses, one per line. The names are read one at a time into the string called name using getline( ). Then a temporary file called m.txt is created to build the customized message for that individual; the customization is the note about how to remove themselves, along with the URL. Then the message body, which is in the file specified by the second command-line argument, is appended to m.txt. Finally, the command is built inside a string: the “-F” argument to fastmail is who it’s from, the “-r” argument is who to reply to. The “-s” is the subject line, the next argument is the file containing the mail and the last argument is the email address to send it to.
You can start this program in the background and tell Unix not to stop the program when you sign off of the server. However, it takes a while to run for a long list (this isn’t because of the program itself, but the mailing process). I like to keep track of the progress of the program by sending a status message to another email account, which is accomplished in the last few lines of the program.
A general information-extraction CGI program
One of the problems with CGI is that you must write and compile a new program every time you want to add a new facility to your Web site. However, much of the time all that your CGI program does is capture information from the user and store it on the server. If you could use hidden fields to specify what to do with the information, then it would be possible to write a single CGI program that would extract the information from any CGI request. This information could be stored in a uniform format, in a subdirectory specified by a hidden field in the HTML form, and in a file that included the user’s email address – of course, in the general case the email address doesn’t guarantee uniqueness (the user may post more than one submission) so the date and time of the submission can be mangled in with the file name to make it unique. If you can do this, then you can create a new data-collection page just by defining the HTML and creating a new subdirectory on your server. For example, every time I come up with a new class or workshop, all I have to do is create the HTML form for signups – no CGI programming is required.
The following HTML page shows the format for this scheme. Since a CGI POST is more general and doesn’t have any limit on the amount of information it can send, it will always be used instead of a GET for the ExtractInfo.cpp program that will implement this system. Although this form is simple, yours can be as complicated as you need it.
//:! C10:INFOtest.html <html><head><title>
Extracting information from an HTML POST</title> </head>
<body bgcolor="#FFFFFF" link="#0000FF" vlink="#800080"> <hr>
<p>Extracting information from an HTML POST</p>
Appendix B: Programming Guidelines
560
<form action="/cgi-bin/ExtractInfo.exe" method="POST">
<input type="hidden" name="subject-field" value="test-extract-info">
<input type="hidden" name="reminder" value="Remember your lunch!">
<input type="hidden" name="test-field" value="on">
<input type="hidden" name="mail-copy" value="Bruce@EckelObjects.com;eckel@aol.com"> <input type="hidden" name="confirmation" value="confirmation1">
<p>Email address (Required): <input type="text" size="45" name="email-address" > </p>Comment:<br>
<textarea name="Comment" rows="6" cols="55"> </textarea>
<p><input type="submit" name="submit"> <input type="reset" name="reset"</p> </form><hr></body></html>
///:~
Right after the form’s action statement, you see
<input type="hidden"
This means that particular field will not appear on the form that the user sees, but the information will still be submitted as part of the data for the CGI program.
The value of this field named “subject-field” is used by ExtractInfo.cpp to determine the subdirectory in which to place the resulting file (in this case, the subdirectory will be “test- extract-info”). Because of this technique and the generality of the program, the only thing you’ll usually need to do to start a new database of data is to create the subdirectory on the server and then create an HTML page like the one above. The ExtractInfo.cpp program will do the rest for you by creating a unique file for each submission. Of course, you can always change the program if you want it to do something more unusual, but the system as shown will work most of the time.
The contents of the “reminder” field will be displayed on the form that is sent back to the user when their data is accepted. The “test-field” indicates whether to dump test information to the resulting Web page. If “mail-copy” exists and contains anything other than “no” the value string will be parsed for mailing addresses separated by ‘;’ and each of these addresses will get a mail message with the data in it. The “email-address” field is required in each case and the email address will be checked to ensure that it conforms to some basic standards.
The “confirmation” field causes a second program to be executed when the form is posted. This program parses the information that was stored from the form into a file, turns it into
Appendix B: Programming Guidelines
561
human-readable form and sends an email message back to the client to confirm that their information was received (this is useful because the user may not have entered their email address correctly; if they don’t get a confirmation message they’ll know something is wrong). The design of the “confirmation” field allows the person creating the HTML page to select more than one type of confirmation. Your first solution to this may be to simply call the program directly rather than indirectly as was done here, but you don’t want to allow someone else to choose – by modifying the web page that’s downloaded to them – what programs they can run on your machine.
Here is the program that will extract the information from the CGI request:
//: C10:ExtractInfo.cpp
//Extracts all the information from a CGI POST
//submission, generates a file and stores the
//information on the server. By generating a
//unique file name, there are no clashes like
//you get when storing to a single file. #include "CGImap.h"
#include <iostream> #include <fstream> #include <cstdio> #include <ctime> using namespace std;
const string contact("Bruce@EckelObjects.com");
//Paths in this program are for Linux/Unix. You
//must use backslashes (two for each single
//slash) on Win32 servers:
const string rootpath("/home/eckel/");
void show(CGImap& m, ostream& o);
//The definition for the following is the only
//thing you must change to customize the program void
store(CGImap& m, ostream& o, string nl = "\n");
int main() {
cout << "Content-type: |
text/html\n"<< endl; |
Post p; // Collect the |
POST data |
CGImap query(p); |
|
// "test-field" set to |
"on" will dump contents |
if(query["test-field"] |
== "on") { |
cout << "map size: " |
<< query.size() << "<br>"; |
query.dump(cout); |
|
Appendix B: Programming Guidelines
562
}
if(query["subject-field"].size() == 0) { cout << "<h2>Incorrect form. Contact " << contact << endl;
return 0;
}
string email = query["email-address"]; if(email.size() == 0) {
cout << "<h2>Please enter your email address" << endl;
return 0;
}
if(email.find_first_of(" \t") != string::npos){ cout << "<h2>You cannot include white space "
"in your email address" << endl; return 0;
}
if(email.find('@') == string::npos) {
cout << "<h2>You must include a proper email" " address including an '@' sign" << endl;
return 0;
}
if(email.find('.') == string::npos) {
cout << "<h2>You must include a proper email" " address including a '.'" << endl;
return 0;
}
//Create a unique file name with the user's
//email address and the current time in hex const int bsz = 1024;
char fname[bsz]; time_t now;
time(&now); // Encoded date & time sprintf(fname, "%s%X.txt", email.c_str(), now); string path(rootpath + query["subject-field"] +
"/" + fname);
ofstream out(path.c_str()); if(!out) {
cout << "cannot open " << path << "; Contact"
<<contact << endl;
return 0;
}
// Store the file and path information:
Appendix B: Programming Guidelines
563
out << "///{" << path << endl; // Display optional reminder:
if(query["reminder"].size() != 0)
cout <<"<H1>" << query["reminder"] <<"</H1>"; show(query, cout); // For results page store(query, out); // Stash data in file
cout << "<br><H2>Your submission has been " "posted as<br>" << fname << endl
<< "<br>Thank you</H2>" << endl; out.close();
//Optionally send generated file as email
//to recipients specified in the field: if(query["mail-copy"].length() != 0 &&
query["mail-copy"] != "no") { string to = query["mail-copy"];
//Parse out the recipient names, separated
//by ';', into a vector.
vector<string> recipients; int ii = to.find(';'); while(ii != string::npos) {
recipients.push_back(to.substr(0, ii)); to = to.substr(ii + 1);
ii = to.find(';');
}
recipients.push_back(to); // Last one
// "fastmail" only available on Linux/Unix: for(int i = 0; i < recipients.size(); i++) {
string cmd("fastmail -s"" \"" + query["subject-field"] + "\" " + path + " " + recipients[i]);
system(cmd.c_str());
}
}
//Execute a confirmation program on the file.
//Typically, this is so you can email a
//processed data file to the client along with
//a confirmation message: if(query["confirmation"].length() != 0) {
string conftype = query["confirmation"]; if(conftype == "confirmation1") {
string command("./ProcessApplication.exe "+ path + " &");
//The data file is the argument, and the
Appendix B: Programming Guidelines
564
// ampersand runs it as a separate process: system(command.c_str());
string logfile("Extract.log"); ofstream log(logfile.c_str());
}
}
}
//For displaying the information on the html
//results page:
void show(CGImap& m, ostream& o) { string nl("<br>");
o << "<h2>The data you entered was:"
<<"</h2><br>"
<<"From[" << m["email-address"] << ']' <<nl; for(CGImap::iterator it = m.begin();
it != m.end(); it++) { string name = (*it).first,
value = (*it).second; if(name != "email-address" &&
name != "confirmation" && name != "submit" &&
name != "mail-copy" && name != "test-field" && name != "reminder")
o << "<h3>" << name << ": </h3>"
<<"<pre>" << value << "</pre>";
}
}
// Change this to customize the program:
void store(CGImap& m, ostream& o, string nl) {
o << "From[" << m["email-address"] << ']' <<nl; for(CGImap::iterator it = m.begin();
it != m.end(); it++) { string name = (*it).first,
value = (*it).second; if(name != "email-address" &&
name != "confirmation" && name != "submit" &&
name != "mail-copy" && name != "test-field" && name != "reminder")
Appendix B: Programming Guidelines
565