CS 541 Lecture -*- Outline -*- * templates essentially macros for classes ** example: lists *** abstract base class -------------------------- // FILE: CookList.h // Lists based on the Procedural Data // Abstraction idea, as described in the // paper: "Object-Oriented Programming // Versus Abstract Data Types", by Cook #include "boolean.h" #include "exception.h" // List is an abstract class // of homogeneous lists of type T elements template class List { public: virtual bool is_null() const = 0; virtual T head() const = 0; virtual List* tail() const = 0; // convenience functions T car() const { return head(); } List* cdr() const { return tail(); } }; ------------------------------ explain abstract base classes, const, virtual, = 0, template *** concrete classes for each constructor **** nil ----------------------------- // a class of exceptions (not used) class Empty{}; // The empty list of type T // The two hacks below are to get around // the C++ type checker, which doesn't // understand that an abort never returns. template class Nil : public List { public: Nil() {} bool is_null() const { return true; } T head() const { throw(Empty()); return (T)0; // never reached } List* tail() const { throw(Empty()); return 0; // never reached } }; ------------------------------ no rep needed the (T)0 is to satisfy the type system **** nonempty lists ------------------------------ // non-empty lists of type T template class Cons : public List { public: Cons(T x, List* l) { : contents(x), next_cell(l) {} is_null() const { return false; } T head() const { return contents; } List* tail() const { return next_cell; } private: T contents; List * next_cell; }; ----------------------------- ** template functions --------------------------- #include template extern ostream& operator << (ostream & out, const List& lst); --------------------------- ** higher order function template classes the idea of this is that a higher order function is represented by a class the function and other parameters are made into pure virtual operations to pass a function parameter, one makes up a subclass; if one needs local data for the closure, then one can store the data in data members which are filled in by a constructor. *** example declarations --------------------------- template class List_for_each { public: void for_each(const List& lst); virtual void the_proc(T) = 0; }; ---------------------------- the parameter is the_proc ---------------------------- template class List_map { public: List* map(const List& lst); virtual S the_proc(T) = 0; }; template class List_flat_recur { public: S flat_recur(const List& lst); virtual S seed() = 0; virtual S the_proc(T, S) = 0; }; --------------------------- *** implementations --------------------------- // FILE: CookList.C // The higher order functions for lists #include "CookList.h" template void List_for_each::for_each (const List& lst) { const List *it = &lst; while (!(it->is_null())) { the_proc(it->head()); it = it->tail(); } } --------------------------- note the calls to the_proc explain the loop control, the pointer stuff --------------------------- template List* List_map::map (const List& lst) { if (lst.is_null()) { return new Nil(); } else { return new Cons (the_proc(lst.head()), map(*(lst.tail()))); } } template S List_flat_recur::flat_recur (const List& lst) { if (lst.is_null()) { return seed(); } else { return the_proc (lst.head(), flat_recur(*(lst.tail()))); } } --------------------------- *** use of higher-order classes --------------------------- // The following class is only used // in the implementation of operator << template /* static */ class print_it_closure : public List_for_each { public: print_it_closure(ostream & out) : rep(out) {} void the_proc(T x) { rep << " " << x; } private: ostream& rep; }; template ostream& operator << (ostream & out, const List& lst){ out << "("; if (~(lst.is_null())) { out << lst.head(); print_it_closure(out).for_each (*(lst.tail())); }; out << ")"; return out; } ------------------------- ** main program using templates -------------------- // FILE: CookMain.C #include #include "CookList.h" // An object of type inc_closure has // an operation map that will map adding 1 // over a list of integers. class inc_closure : public List_map { public: inc_closure() {} int the_proc(int x) { return x+1;} }; // An object of type sum_closure has // an operation flat_recur that will // add up a list of integers. class sum_closure : public List_flat_recur { public: sum_closure() {} int seed() { return 0; } int the_proc(int x, int y) { return x + y; } }; ------------------------ some more higher order stuff ----------------------- int main() { List* my_list; my_list = new Nil(); my_list = new Cons (3, new Cons (4, new Cons (5, new Nil()))); cout << *my_list << "\n" << flush; inc_closure ic; cout << *(ic.map(*my_list)) << "\n" << flush; sum_closure sc; cout << sc.flat_recur(*my_list) << "\n" << flush; return 0; } ----------------------