home *** CD-ROM | disk | FTP | other *** search
- _COLLECTIONS IN TURBO C++_
- by Bruce Eckel
-
-
- [LISTING ONE]
-
- // COLLECT.CPP : collection example, with multiple inheritance and
- // pointers to members.
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
-
- struct item {
- virtual ~item() {} // so collection can properly destroy what it holds
- };
-
- // Suppose we have a pre-existing class to hold items:
- class collection {
- // in 2.0, "holder" is a global name. In 2.1, it's hidden:
- struct holder {
- holder * next; // link to next holder
- item * data; // pointer to actual data
- holder(holder * nxt, item * dat) // constructor
- : next(nxt), data(dat) {}
- } *head, *cursor;
- public:
- // initialize an empty list:
- collection() : head((holder *)NULL), cursor((holder *)NULL) {}
- // clean up the list by removing all the elements:
- ~collection() {
- cursor = head;
- while(cursor) { // while the list is not empty ...
- delete cursor->data; // delete the current data
- head = cursor->next; // head keeps track of where the next holder is
- delete cursor; // delete the current holder
- cursor = head; // move to the next holder
- }
- }
- // Paste a new item in at the top (this is tricky):
- void add(item * i) { head = new holder(head, i); }
- // reset() and next() return the current item or null, for the list's end:
- item * reset() { // go back to the top of the list
- cursor = head;
- // the list may be empty; only return data if cursor isn't null
- return cursor ? cursor->data : (item *)NULL;
- }
- item * next() {
- // Only move forward if cursor isn't null:
- if(cursor) cursor = cursor->next;
- // only return data if cursor isn't null:
- return cursor ? cursor->data : (item *)NULL;
- }
- };
-
- // Now suppose we have a second pre-exising class to hold words,
- // keep track of word counts, and print itself in 2 ways:
- class word {
- char * w;
- int count;
- public:
- word(char * wd) : count(1) {
- w = new char[strlen(wd) + 1]; // allocate space for word
- strcpy(w,wd); // copy it in
- }
- ~word() {
- printf("%s : %d occurrences\n", w, count);
- delete w; // free space for word
- }
- int compare(char * testword) {
- int match = !strcmp(w, testword);
- if(match) count++; // count testword if it matches
- return match;
- }
- void print1() { printf("%s\n", w); } // 1 per line
- void print2();
- };
-
- // Zortech 2.06 and cfront 2.0 wouldn't allow the following
- // function as an inline; Turbo C++ would.
- void word::print2() { // print several words per line
- static p1cnt; // count words on a line
- const words_per_line = 7;
- printf("%s ", w);
- if(++p1cnt % words_per_line) return;
- putchar('\n'); // only when remainder is 0
- }
-
- // What if we want to make a collection of words? Multiple
- // Inheritance to the rescue:
- class worditem : public item, public word {
- public:
- worditem(char * wrd) : word(wrd) {}
- };
-
- // now we can create a collection of worditems. Here's an array
- // of words to put in our collection:
- char * words[] = { "this", "is", "a", "test", "of", "worditem" };
-
- #ifdef TEST1
- main() {
- collection c;
- for(int i = 0; i < sizeof(words)/sizeof(words[0]); i++)
- c.add(new worditem(words[i]));
- // NOTE: Zortech C++ 2.06 doesn't work here.
- }
- #endif // TEST1
-
- // But now we want to count instances of words. We need to modify the
- // collection class so it conditionally adds a word, or just counts it
- // if it already exists:
-
- class wordcounter : public collection {
- public:
- // Customize for worditems (no overhead):
- void add(worditem * wi) { collection::add(wi); }
- worditem * reset() { return (worditem *)collection::reset(); }
- worditem * next() { return (worditem *)collection::next(); }
- void add_or_count(char * newword) {
- worditem * cur = reset();
- while(cur) {
- // if found, increment the count and quit the search:
- if(cur->compare(newword)) return;
- cur = next();
- }
- // at this point, we didn't find it, so add it to the list:
- add(new worditem(newword));
- }
- // Pointers to members (Zortech 2.06 doesn't support this):
- void apply(void (word::*pmf)()) {
- worditem * wit = reset();
- while(wit) { // do while list is not empty
- (wit->*pmf)(); // dereference member function pointer
- wit = next(); // get next list element
- }
- }
- };
-
- char * words2[] = { "this", "this", "is", "a", "test", "test", "test" };
-
- #ifdef TEST2
- main() {
- wordcounter wc;
- for(int i = 0; i < sizeof(words2)/sizeof(words2[0]); i++)
- wc.add_or_count(words2[i]);
- // Now "apply" two different functions to the list:
- wc.apply(&word::print1);
- wc.apply(&word::print2); putchar('\n');
- }
- #endif // TEST2
-
- // Now, for fun, let's use this class to count the keywords and
- // identifiers in a C++ program. Try this program on itself:
- // collect < collect.cpp > count
- // Look up strstr(), strchr() and strtok() in your ANSI C
- // library guide.
-
- const char * delimiters = " \t#/(){}[]<>.,;:*+-~!%^&=\\|?\'\"";
- const char * digits = "0123456789.";
-
- #ifdef TEST3
- main() { // use i/o redirection on the command line.
- wordcounter symbol_table;
- char buf[120];
- while (gets(buf)) { // get from standard input
- if(*buf == '#') continue; // ignore preprocessor lines
- // strip all quoted strings in the line:
- char * quote = strchr(buf, '\"'); // find first quoted string
- while(quote) {
- if(quote[-1] == '\\') break; // for \" literal quote
- *quote++ = ' '; // erase quote
- while(*quote != '\"' && *quote != 0)
- *quote++ = ' '; // erase contents of string
- *quote = ' '; // erase ending quote
- quote = strchr(quote, '\"'); // look for next quoted string
- }
- char * cmt = strstr(buf, "//"); // C++-style comments only
- if(cmt) *cmt = 0; // strip comments by terminating string
- puts(buf); // Look at the modified string
- char * token; // strtok uses delimiters to find a token:
- if((token = strtok(buf, delimiters)) != NULL){ // first strtok call
- if(strcspn(token, digits)) // ignore constants
- symbol_table.add_or_count(token);
- // subsequent strtok calls for the same input line:
- while((token = strtok(0, delimiters)) != NULL)
- if(strcspn(token, digits)) // ignore constants
- symbol_table.add_or_count(token);
- }
- } // print the list in 2 ways, using pointers to members:
- symbol_table.apply(&word::print1);
- symbol_table.apply(&word::print2); putchar('\n');
- } // results are output by the destructor calls
- #endif // TEST3
-
-
- [Example 1: Macro that puts void at the beginning of a line, then
- returns to the point where you invoked it.]
-
- macro InsertVoid
- SetMark(0); /* save our place */
- LeftOfLine;
- InsertText("void ");
- MoveToMark(0); /* restore the place */
- end; /* end of macro InsertVoid */
-
- Alt-V : InsertVoid; /* bind to key */
-
-
- [Example 2: Typical collection]
-
- item * i = c.reset();
- while(i) {
- // do something
- i = c.next();
- }
-
-