home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / magazine / drdobbs / 1990 / 08 / eckel.lst < prev    next >
File List  |  1990-06-20  |  7KB  |  216 lines

  1. _COLLECTIONS IN TURBO C++_
  2. by Bruce Eckel
  3.  
  4.  
  5. [LISTING ONE]
  6.  
  7. // COLLECT.CPP : collection example, with multiple inheritance and 
  8. // pointers to members.
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <stdlib.h>
  12.  
  13. struct item {
  14.   virtual ~item() {} // so collection can properly destroy what it holds
  15. };
  16.  
  17. // Suppose we have a pre-existing class to hold items:
  18. class collection {
  19.   // in 2.0, "holder" is a global name.  In 2.1, it's hidden:
  20.   struct holder {
  21.     holder * next;  // link to next holder
  22.     item * data; // pointer to actual data
  23.     holder(holder * nxt, item * dat)  // constructor
  24.       : next(nxt), data(dat) {}
  25.   } *head, *cursor;
  26. public:
  27.   // initialize an empty list:
  28.   collection() : head((holder *)NULL), cursor((holder *)NULL) {}
  29.   // clean up the list by removing all the elements:
  30.   ~collection() {
  31.     cursor = head;
  32.     while(cursor) {  // while the list is not empty ...
  33.       delete cursor->data;  // delete the current data
  34.       head = cursor->next;  // head keeps track of where the next holder is
  35.       delete cursor;  // delete the current holder
  36.       cursor = head; // move to the next holder
  37.     }
  38.   }
  39.   // Paste a new item in at the top (this is tricky):
  40.   void add(item * i) { head = new holder(head, i); }
  41.   // reset() and next() return the current item or null, for the list's end:
  42.   item * reset() { // go back to the top of the list
  43.     cursor = head; 
  44.     // the list may be empty; only return data if cursor isn't null
  45.     return cursor ? cursor->data : (item *)NULL;
  46.   }
  47.   item * next() {
  48.     // Only move forward if cursor isn't null:
  49.     if(cursor) cursor = cursor->next;
  50.     // only return data if cursor isn't null:
  51.     return cursor ? cursor->data : (item *)NULL;
  52.   }
  53. };
  54.  
  55. // Now suppose we have a second pre-exising class to hold words,
  56. // keep track of word counts, and print itself in 2 ways:
  57. class word {
  58.   char * w;
  59.   int count;
  60. public:
  61.   word(char * wd) : count(1) {
  62.     w = new char[strlen(wd) + 1];  // allocate space for word
  63.     strcpy(w,wd);  // copy it in
  64.   }
  65.   ~word() {
  66.     printf("%s : %d occurrences\n", w, count);
  67.     delete w;  // free space for word
  68.   }
  69.   int compare(char * testword) {
  70.     int match = !strcmp(w, testword);
  71.     if(match) count++; // count testword if it matches
  72.     return match;
  73.   }
  74.   void print1() { printf("%s\n", w); } // 1 per line
  75.   void print2();
  76. };
  77.  
  78. // Zortech 2.06 and cfront 2.0 wouldn't allow the following
  79. // function as an inline; Turbo C++ would.
  80. void word::print2() {  // print several words per line
  81.   static p1cnt;  // count words on a line
  82.   const words_per_line = 7;
  83.   printf("%s ", w);
  84.   if(++p1cnt % words_per_line) return;
  85.   putchar('\n'); // only when remainder is 0
  86. }
  87.  
  88. // What if we want to make a collection of words?  Multiple
  89. // Inheritance to the rescue:
  90. class worditem : public item, public word {
  91. public:
  92.   worditem(char * wrd) : word(wrd) {}
  93. };
  94.  
  95. // now we can create a collection of worditems.  Here's an array
  96. // of words to put in our collection:
  97. char * words[] = { "this", "is", "a", "test", "of", "worditem" };
  98.  
  99. #ifdef TEST1
  100. main() {
  101.   collection c;
  102.   for(int i = 0; i < sizeof(words)/sizeof(words[0]); i++)
  103.     c.add(new worditem(words[i]));
  104.   // NOTE: Zortech C++ 2.06 doesn't work here.
  105. }
  106. #endif // TEST1
  107.  
  108. // But now we want to count instances of words.  We need to modify the
  109. // collection class so it conditionally adds a word, or just counts it
  110. // if it already exists:
  111.  
  112. class wordcounter : public collection {
  113. public:
  114.   // Customize for worditems (no overhead):
  115.   void add(worditem * wi) { collection::add(wi); }
  116.   worditem * reset() { return (worditem *)collection::reset(); }
  117.   worditem * next() { return (worditem *)collection::next(); }
  118.   void add_or_count(char * newword) {
  119.     worditem * cur = reset();
  120.     while(cur) {
  121.       // if found, increment the count and quit the search:
  122.       if(cur->compare(newword)) return;
  123.       cur = next();
  124.     }
  125.     // at this point, we didn't find it, so add it to the list:
  126.     add(new worditem(newword));
  127.   }
  128.   // Pointers to members (Zortech 2.06 doesn't support this):
  129.   void apply(void (word::*pmf)()) {
  130.     worditem * wit = reset();
  131.     while(wit) {  // do while list is not empty
  132.       (wit->*pmf)();  // dereference member function pointer
  133.       wit = next();  // get next list element
  134.     }
  135.   }
  136. };
  137.  
  138. char * words2[] = { "this", "this", "is", "a", "test", "test", "test" };
  139.  
  140. #ifdef TEST2
  141. main() {
  142.   wordcounter wc;
  143.   for(int i = 0; i < sizeof(words2)/sizeof(words2[0]); i++)
  144.     wc.add_or_count(words2[i]);
  145.   // Now "apply" two different functions to the list:
  146.   wc.apply(&word::print1);
  147.   wc.apply(&word::print2); putchar('\n');
  148. }
  149. #endif // TEST2
  150.  
  151. // Now, for fun, let's use this class to count the keywords and
  152. // identifiers in a C++ program.  Try this program on itself:
  153. //    collect < collect.cpp > count
  154. // Look up strstr(), strchr() and strtok() in your ANSI C
  155. // library guide.
  156.  
  157. const char * delimiters = " \t#/(){}[]<>.,;:*+-~!%^&=\\|?\'\"";
  158. const char * digits = "0123456789.";
  159.  
  160. #ifdef TEST3
  161. main() { // use i/o redirection on the command line.
  162.   wordcounter symbol_table;
  163.   char buf[120];
  164.   while (gets(buf)) { // get from standard input
  165.     if(*buf == '#') continue;  // ignore preprocessor lines
  166.     // strip all quoted strings in the line:
  167.     char * quote = strchr(buf, '\"');  // find first quoted string
  168.     while(quote) {
  169.       if(quote[-1] == '\\') break; // for \" literal quote
  170.       *quote++ = ' ';  // erase quote
  171.       while(*quote != '\"' && *quote != 0)
  172.         *quote++ = ' '; // erase contents of string
  173.       *quote = ' '; // erase ending quote
  174.       quote = strchr(quote, '\"'); // look for next quoted string
  175.     }
  176.     char * cmt = strstr(buf, "//");  // C++-style comments only
  177.     if(cmt) *cmt = 0; // strip comments by terminating string
  178.     puts(buf);  // Look at the modified string
  179.     char * token;  // strtok uses delimiters to find a token:
  180.     if((token = strtok(buf, delimiters)) != NULL){  // first strtok call
  181.       if(strcspn(token, digits))  // ignore constants
  182.         symbol_table.add_or_count(token);
  183.        // subsequent strtok calls for the same input line:
  184.       while((token = strtok(0, delimiters)) != NULL)
  185.         if(strcspn(token, digits))  // ignore constants
  186.           symbol_table.add_or_count(token);
  187.     }
  188.   }  // print the list in 2 ways, using pointers to members:
  189.   symbol_table.apply(&word::print1);
  190.   symbol_table.apply(&word::print2); putchar('\n');
  191. }  // results are output by the destructor calls
  192. #endif // TEST3
  193.  
  194.  
  195. [Example 1: Macro that puts void at the beginning of a line, then 
  196. returns to the point where you invoked it.]
  197.  
  198. macro InsertVoid
  199.       SetMark(0);  /* save our place */
  200.       LeftOfLine;
  201.       InsertText("void ");
  202.       MoveToMark(0);  /* restore the place */
  203. end; /* end of macro InsertVoid */
  204.  
  205. Alt-V : InsertVoid;   /* bind to key */
  206.  
  207.  
  208. [Example 2: Typical collection]
  209.  
  210. item * i = c.reset();
  211. while(i) {
  212.   // do something
  213.   i = c.next();
  214. }
  215.  
  216.