// fakelist.h see license.txt for copyright and terms of use // headerless list of nodes where each node has a 'next' field #ifndef FAKELIST_H #define FAKELIST_H // idea: define a templatized class such that a pointer to // this class appears to present access to a list of the // underlying T objects; but in fact, the pointer is actually // a T pointer, and T contains a field called 'next' which // inductively defines the list contents. then such a pointer // could be used to provide documentation of the presence of // the list (and not just one node), and a uniform (with other // list interfaces) syntactic interface, while hiding an // efficient representation // why not just insist that all objects be derived from some // base class, e.g. FakeListNode, that defines 'next'? because // what is the type of 'next'? it has to be FakeListNode*, but // then either it must be physically first, or else 'next' is // a pointer to the interior and I have to worry about whether // the casts to/from the outer type will be correctly offset; and // then getting to the next node requires a cast (the usual // problem with subtyping polymorphism) // for now, the list is non-owning (unless you call 'deallocNodes') class Some_undefined_class; template class FakeList { private: // you can't create or delete one of these FakeList(); ~FakeList(); // silence a silly egcs-1.1.2 warning (this class isn't defined) friend class Some_undefined_class; // this class has *no* data--an object of this type is // never actually created! instead we play with pointers // to this "type", and cast to T* as necessary public: // this is as much of a constructor as there is static FakeList *makeList(T *node) { return (FakeList*)node; } static FakeList *emptyList() { return NULL; } // this will deallocate all the nodes in the list; the list itself // is, therefore, also deallocated and should not be used after this void deallocNodes(); // simple selectors int count() const; bool isEmpty() const { return this == NULL; } bool isNotEmpty() const { return !isEmpty(); } // "car" in Lisp terminology T *first() { return (T*)this; } T const *firstC() const { return (T const*)this; } // "cdr" in Lisp terminology FakeList *butFirst() { return makeList(first()->next); } FakeList const *butFirstC() const { return makeList(firstC()->next); } // similar to "cons" in Lisp terminology (but this doesn't allocate) FakeList *prepend(T *newHead) { // I'm going to be surprised if this is ever not true.. it's // a potential problem in cc.gr, since I'm assuming there that // 'newHead' is not already on any other lists... // // update: This does occasionally get triggered, because a node // might get yielded to two contexts. Failing this assertion is a // symptom that the sharing needs to be more carefully managed. // It's often the case that newHead->next in fact equals first() // already, but if the client code wants to let that slide it's // going to have to check itself; I don't want to silently allow // accidental happens-to-not-change-anything overwriting down in // this code. xassert(newHead->next == NULL); newHead->next = first(); return makeList(newHead); } // random access (linear time of course) T const *nthC(int n) const; T *nth(int n) { return const_cast(nthC(n)); } // don't add an 'append' method; I think if you're trying to append // with FakeLists then you're probably misusing them // perhaps breaking the idea a little... FakeList *reverse(); // this class is deliberately sparse on methods for now, since I'm // not sure what kind of manipulations I'll want, given that this // class's purpose is fairly specialized (AST lists) }; // I'm deliberately contradicting the convention elsewhere, where // "FOREACH" comes first; I think it should have come second to begin // with, and since this class isn't derived from any of the others // with the opposite convention, this is as good a place as any to // reverse it #define FAKELIST_FOREACH(NodeType, listPtr, nodePtrVar) \ for (NodeType const *nodePtrVar = listPtr->firstC(); \ nodePtrVar != NULL; \ nodePtrVar = nodePtrVar->next) #define FAKELIST_FOREACH_NC(NodeType, listPtr, nodePtrVar) \ for (NodeType *nodePtrVar = listPtr->first(); \ nodePtrVar != NULL; \ nodePtrVar = nodePtrVar->next) template void FakeList::deallocNodes() { T *p = first(); while (p) { T *next = p->next; // just in case T's destructor thinks it owns 'next', // nullify it since I'm going to dealloc it myself p->next = NULL; delete p; p = next; } } template int FakeList::count() const { int ct = 0; FAKELIST_FOREACH(T, this, p) { ct++; } return ct; } template T const *FakeList::nthC(int n) const { const FakeList *p = this; while (n > 0) { p = p->butFirstC(); // segfaults if n is too small n--; } return p->firstC(); } template FakeList *FakeList::reverse() { FakeList *src = this; FakeList *dest = emptyList(); while (src->isNotEmpty()) { // remove first element of 'src' T *first = src->first(); src = src->butFirst(); first->next = NULL; // put it at the head of 'dest' dest = dest->prepend(first); } return dest; } #endif // FAKELIST_H