Category : Files from Magazines
Archive   : DDJ-9008.ZIP
Filename : ECKEL.LST

 
Output of file : ECKEL.LST contained in archive : DDJ-9008.ZIP
_COLLECTIONS IN TURBO C++_
by Bruce Eckel


[LISTING ONE]

// COLLECT.CPP : collection example, with multiple inheritance and
// pointers to members.
#include
#include
#include

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();
}



  3 Responses to “Category : Files from Magazines
Archive   : DDJ-9008.ZIP
Filename : ECKEL.LST

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/