_GENERIC CONTAINERS IN C++_ by Andrew Davidson /* * Generic list * * by A. E. Davidson 12/90 * * Overview * * USES * to declare a generic list of class * foo items and a generic list of class * bar items * * #include "genriclt.hh" * DeclareList(Foo); * DeclareList(Bar); * * main() * { * class foo; * class bar; * GenList(foo) genListOfFoo; * GenList(bar) genListOfBar; * GenIter * * REQUIREMENTS / ASSUMPTIONS * The generic list does not manage * the items memory. It is up to the * user to free them * * the function GenList::RemoveItem() * takes a pointer to a member function. * this funciton should return true is found * else false. GenList::RemoveItem will not * compile on my machine if the member * fuction is inlined. it gives a signal 6 error message * * NOTES * never use new to create a list or an iter! * * */ #include #ifndef GENERIC_LIST_HH #define GENERIC_LIST_HH /* * these macro's should be used * any where you need to reffer to * the generic list, item, or iter classes * * PTAMF: Pointer to a Member Function */ #define PTAMF(CLASS_TAG) name2(CLASS_TAG,PTAMF) #define GenItem(CLASS_TAG) name2(CLASS_TAG,GenItem) #define GenList(CLASS_TAG) name2(CLASS_TAG,GenList) #define GenIter(CLASS_TAG) name2(CLASS_TAG,GenIter) /*----------------------------- class Item ---------------------------*/ /* * GenItem(CLASS_TAG) is a private * class. It can only be created by * be the member functions of GenList(CLASS_TAG) * and GenIter(CLASS_TAG) */ #define GenItemdeclare(CLASS_TAG) \ class GenItem(CLASS_TAG) \ { \ GenItem(CLASS_TAG) *next; \ CLASS_TAG &item; \ GenItem(CLASS_TAG)(CLASS_TAG &i) : item(i) \ {next = NULL;} \ GenItem(CLASS_TAG)(CLASS_TAG &i, GenItem(CLASS_TAG) *n) : item(i) \ {next = n; } \ ~GenItem(CLASS_TAG)() \ {;} \ friend GenList(CLASS_TAG); \ friend GenIter(CLASS_TAG); \ } /*---------------------------- class List ---------------------------*/ #define GenListdeclare(CLASS_TAG) \ class GenList(CLASS_TAG) \ { \ GenItem(CLASS_TAG) *hd; \ public: \ GenList(CLASS_TAG)(GenItem(CLASS_TAG) *n = NULL) \ { hd = n;} \ GenList(CLASS_TAG)(const CLASS_TAG &i) \ {hd = NULL; insert(i);} \ ~GenList(CLASS_TAG)() \ {Clear();} \ void Clear(void) \ { \ GenItem(CLASS_TAG) *pt; \ while (hd) \ { \ pt = hd; \ hd = hd->next; \ delete pt; \ } \ } \ GenList(CLASS_TAG)(const GenList(CLASS_TAG) &seq) {hd = seq.hd;} \ GenList(CLASS_TAG) operator = (const GenList(CLASS_TAG) &other) \ { \ hd = other.hd; \ return *this; \ } \ void insert(CLASS_TAG &i){ hd = new GenItem(CLASS_TAG)(i, hd); } \ void append(CLASS_TAG &i) \ { \ for (GenItem(CLASS_TAG) *pt = hd; pt && pt ->next; pt = pt->next) \ ; \ if (pt) \ { \ GenItem(CLASS_TAG) *tmp = new GenItem(CLASS_TAG)(i); \ pt->next = tmp; \ } \ else insert(i); \ } \ \ void removeItem(PTAMF(CLASS_TAG) found, CLASS_TAG &obj) \ { \ GenItem(CLASS_TAG) *prev, *rem; \ \ prev = NULL; \ rem = hd; \ while( rem && !(obj.*found)(rem->item) ) \ { \ prev = rem; \ rem = rem->next; \ } \ if (rem) \ { \ if (prev) \ prev->next = rem->next; \ else \ hd = rem->next; \ delete rem; \ } \ } \ friend GenIter(CLASS_TAG); \ } /*------------------------------- class Iter ---------------------------*/ /* * interate over entire list * CLASS_TAG *operator()() */ #define GenIterdeclare(CLASS_TAG) \ class GenIter(CLASS_TAG) \ { \ GenItem(CLASS_TAG) *current; \ public: \ GenIter(CLASS_TAG)(GenList(CLASS_TAG) &ilist) \ { current = ilist.hd; } \ GenIter(CLASS_TAG)(GenIter(CLASS_TAG) &other) \ { current = other.current; } \ ~GenIter(CLASS_TAG)() \ {;} \ GenIter(CLASS_TAG) operator = (GenList(CLASS_TAG) &ilist) \ { current = ilist.hd; return *this; } \ CLASS_TAG *operator()() \ { \ if (current) \ { \ GenItem(CLASS_TAG) *tmp = current; \ current = current->next; \ return &tmp->item; \ } \ return NULL; \ } \ } /* * macro that create all the generic types * provided for ease of uses * * for some unknown reason my compiler can't handle a * function prameter that is a pointer to a member function * It can deal with it if the pointer is declared using * a typedef */ #define DeclareList(CLASS_TAG) \ typedef int (CLASS_TAG::*PTAMF(CLASS_TAG))(CLASS_TAG &); \ class GenList(CLASS_TAG); \ class GenIter(CLASS_TAG); \ declare(GenItem,CLASS_TAG); \ declare(GenList,CLASS_TAG); \ declare(GenIter,CLASS_TAG) #endif [LISTING TWO] /* * test1.cc * * by A. E. Davidson * * Provides a driver to test the generic list */ #include #include "coin.hh" #include "genericl.hh" /* * use the declare macros to * allow creation of list of desired types */ DeclareList(coin); /* * proto typing function using the generic list * stuff must be done after DeclareList() */ void displayAndTotal(GenIter(coin) next_coin); main() { /*--- create some coins -------*/ coin c1 = penny; coin c2 = nickel; coin c3 = dime; coin c4 = quarter; /*------ create a list of coins -----*/ GenList(coin) list_of_coins; list_of_coins.append(c1); list_of_coins.append(c2); list_of_coins.append(c3); list_of_coins.append(c4); /*------- display the list of coins and there total ------*/ displayAndTotal(list_of_coins); /*-------------- remove one of the coins --------------*/ cout << "\n\n list after removing coin c2 \n"; list_of_coins.removeItem(&coin::found, c2); displayAndTotal(list_of_coins); /* * rember: c2 has been removed from the list but it still exists */ cout << "\n\n coin c2 still exists, it was only removed from the list \n"; cout << "coin: " << c2; #ifdef NEVER /* * this is example shows a design flaw * with the generic list assignment operator * * if you delete an object but do not remove it * from the list first you will get a core dump. * The list will contain a dangling reference * * this is becuase I chose to implement the * the list using references instead of copying * the objects. See the discusion at the end of the * article */ coin *c5 = new coin(quarter); list_of_coins.append(*c5); delete c5; displayAndTotal(list_of_coins); #endif } /* * this function illustrates * how to use the GenIter class * * notice that the parmeter list expect * an inter object, but I always pass a list * object */ void displayAndTotal(GenIter(coin) next_coin) { double total = 0.0; coin *tmp; while ((tmp = next_coin())) { /* * coins know how to convert themselves to doubles */ total += *tmp; /* * coins also know how to display themselves */ cout << "coin: " << *tmp << "\ttotal: " << total <<"\n"; } } [LSITING THREE] /* * coin class * * by A. E. Davidson * * USES * provides a simple class that can be * used to illistrate the operation of * the generic list * * a coin may be a penny, nickel, dime, or quarter */ #ifndef COIN_HH #define COIN_HH enum coin_type {penny, nickel, dime, quarter}; class coin { coin_type unit; double amount; public: coin() {unit = penny; amount = 0.01;} coin( coin_type type); coin(const coin &other) {unit = other.unit; amount = other.amount;} ~coin(){;} coin& operator = (const coin &other) { unit = other.unit; amount = other.amount;} friend ostream& operator << (ostream &os, coin &c); operator double () {return amount;} /* * this function is intended to be * used with GenList(CLASS_TAG)::removeItem() * I get a compile error if I try to inline this * function */ int found(const coin &other); }; #endif [LISTING FOUR] #include "stream.h" #include "coin.hh" char *coin_name[] = {"penny", "nickel", "dime", "quarter"} ; /* * convenient look up table * keeps from having to duplicate case statements any * time you need to work with unit data member */ static struct { coin_type kind; double amount; } table [] = { {penny, 0.01}, {nickel, 0.05}, {dime, 0.10}, {quarter, 0.25}, {quarter, 0.0}, /* end of the table */ }; coin::coin(coin_type type) { unit = type; for (int i = 0; table[i].amount != 0.0 && unit != table[i].kind; i++) ; amount = table[i].amount; } ostream& operator << (ostream &os, coin &c) { os << coin_name[c.unit]; return os; } int coin::found(const coin &other) { return (unit == other.unit); } [LISTING FIVE] /* * this is the output from CPP * g++ -E test1.cc * * I reformated the output to make it * easier to read */ typedef int (coin::* coinPTAMF )(coin &); class coinGenList ; class coinGenIter ; class coinGenItem { coinGenItem *next; coin &item; coinGenItem (coin &i) : item(i) {next = 0 ;} coinGenItem (coin &i, coinGenItem *n) : item(i) {next = n; } ~coinGenItem () {;} friend coinGenList ; friend coinGenIter ; } ; class coinGenList { coinGenItem *hd; public: coinGenList (coinGenItem *n = 0 ) { hd = n;} coinGenList (const coin &i) {hd = 0 ; insert(i);} ~coinGenList () {Clear();} void Clear(void) { coinGenItem *pt; while (hd) { pt = hd; hd = hd->next; delete pt; } } coinGenList (const coinGenList &seq) {hd = seq.hd;} coinGenList operator = (const coinGenList &other) { hd = other.hd; return *this; } void insert(coin &i) { hd = new coinGenItem (i, hd); } void append(coin &i) { for (coinGenItem *pt = hd; pt && pt ->next; pt = pt->next) ; if (pt) { coinGenItem *tmp = new coinGenItem (i); pt->next = tmp; } else insert(i); } void removeItem( coinPTAMF found, coin &obj) { coinGenItem *prev, *rem; prev = 0 ; rem = hd; while( rem && !(obj.*found)(rem->item) ) { prev = rem; rem = rem->next; } if (rem) { if (prev) prev->next = rem->next; else hd = rem->next; delete rem; } } friend coinGenIter ; } ; class coinGenIter { coinGenItem *current; public: coinGenIter (coinGenList &ilist) { current = ilist.hd; } coinGenIter (coinGenIter &other) { current = other.current; } ~coinGenIter () {;} coinGenIter operator = (coinGenList &ilist) { current = ilist.hd; return *this; } coin *operator()() { if (current) { coinGenItem *tmp = current; current = current->next; return &tmp->item; } return 0 ; } } ; void displayAndTotal(coinGenIter next_coin); main() { coin c1 = penny; coin c2 = nickel; coin c3 = dime; coin c4 = quarter; coinGenList list_of_coins; list_of_coins.append(c1); list_of_coins.append(c2); list_of_coins.append(c3); list_of_coins.append(c4); displayAndTotal(list_of_coins); cout << "\n\n list after removing coin c2 \n"; list_of_coins.removeItem(&coin::found, c2); displayAndTotal(list_of_coins); cout << "\n\n coin c2 still exists, it was only removed from the list \n"; cout << "coin: " << c2; # 78 "test1.cc" } void displayAndTotal(coinGenIter next_coin) { double total = 0.0; coin *tmp; while ((tmp = next_coin())) { total += *tmp; cout << "coin: " << *tmp << "\ttotal: " << total <<"\n"; } } [MAKEFILE] CC= g++ OBJS= coin.o test1.o SRCS = coin.cc test1.cc LIBS= -lg++ -lm INCLUDE= -I/n/catserv/usr1/tools/sun4/usr/local/lib/g++-include .SUFFIXES: .cc .cc.o: $(CC) -c -g $< coinTest : $(OBJS) coin.hh genericlt.hh $(CC) -o $@ -g $(OBJS) $(LIBS) clean : rm -f coinTest *.o # # notes # # # $@ the name of the current target # # # $< the name of a dependency file, derived as if selected # for use with an implicit rule # depend : makedepend -- $(CFLAGS) -- $(INCLUDE) -- $(SRCS) # DO NOT DELETE THIS LINE -- make depend depends on it. coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/stream.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/ostream.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/File.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/builtin.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/stddef.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/std.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/stdio.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/math.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/values.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/streambuf.h coin.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/istream.h coin.o: coin.hh test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/stream.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/ostream.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/File.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/builtin.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/stddef.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/std.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/stdio.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/math.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/values.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/streambuf.h test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/istream.h test1.o: coin.hh genericlt.hh test1.o: /n/catserv/usr1/tools/sun4/usr/local/lib/g++-include/generic.h