/* -*- Mode: C++ -*- * Worldvisions Weaver Software: * Copyright (C) 1997-2002 Net Integration Technologies, Inc. * * A linked list container. */ #ifndef __WVLINKLIST_H #define __WVLINKLIST_H #include "wvtypetraits.h" #include "wvsorter.h" /** * @internal * The untyped base class of WvList. * * Putting common code in here allows us to prevent it from being * replicated by each template instantiation of WvList. * */ class WvListBase { WvListBase(const WvListBase &l); // copy constructor - not actually defined anywhere! private: //This is private to force people to pass by reference, not by value WvListBase& operator= (const WvListBase &l); public: WvLink head, *tail; /** Creates an empty linked list. */ WvListBase() : head(NULL, false) { tail = &head; } /** * Returns the number of elements in the list. * * This function causes a full traversal of the list which may be * overly inefficient depending on how and when it is used. * * Returns: the number of elements */ size_t count() const; /** * Reverses the order of elements in the list. * * This function traverses the list and rearranges the pointers * and updates the pointers to head & tail appropriately. * * It does nothing for lists of count<2 */ void reverse(); /** * Quickly determines if the list is empty. * * This is much faster than checking count() == 0. * * Returns: true if empty */ bool isempty() const { return head.next == NULL; } /** * @internal * The untyped base class of WvList::Iter. * * Putting common code in here allows us to prevent it from being * replicated by each template instantiation of WvList. * */ class IterBase { public: const WvListBase *list; WvLink *link, *prev; /** * Binds the iterator to the specified list. * "l" is the list */ IterBase(const WvListBase &l) { list = &l; link = NULL; } /** * Rewinds the iterator to make it point to an imaginary element * preceeding the first element of the list. */ void rewind() // dropping a const pointer here! Danger! { prev = NULL; link = &((WvListBase *)list)->head; } /** * Moves the iterator along the list to point to the next element. * * If the iterator had just been rewound, it now points to the * first element of the list. * * Returns: the current WvLink pointer, or null if there were no * more elements remaining in the traversal sequence */ WvLink *next() { prev = link; return link = link->next; } /** * Returns a pointer to the WvLink at the iterator's current location. * Returns: the current WvLink pointer, or null if there were no * more elements remaining in the traversal sequence */ WvLink *cur() const { return link; } /** * Returns a void pointer to the object at the iterator's current * location. You should almost never need this. Use ptr() instead. */ void *vptr() const { return link->data; } /** * Rewinds the iterator and repositions it over the element that * matches the specified value. * * Uses pointer equality (object identity) as the criteria for * finding the matching element. * * In order to locate multiple matching elements, first call find() * and then use find_next(). * * Returns: the current WvLink pointer, or null if no such element * was found */ WvLink *find(const void *data); /** * Repositions the iterator over the element that matches the * specified value. * * Uses pointer equality (object identity) as the criteria for * finding the matching element. * * Returns: the current WvLink pointer, or null if no such element * was found */ WvLink *find_next(const void*data); }; }; /** * A linked list container class. * * Some rather horrible macros are used to declare actual concrete * list types. * * Example: * * DeclareWvList(WvString); * * int main() * { * WvStringList l; * WvStringList::Iter i(l); * * ... fill the list ... * * i.rewind(); * while (i.next()) * printf("%s\\n", i.str); * } * * * Deallocating list will free all of the WvLinks in the list, but * will only free the user objects that were added with autofree * set to true. * * We need to malloc memory for each WvLink as well as the data it * stores; this is unnecessarily slow. I would rather have made a * base "Link" class for object types that could be stored as links * in a list, and then used object->next instead of all the * List Iterator stuff, but the end result was pure ugliness, so I * gave up. At least this way, the same object can be in multiple * lists. * * List type construction is facilitated by the following macros: * * - DeclareWvList(Type): creates a subclass named TypeList * that contains pointers to Type. * - DeclareWvList2(name, Type): as the above, but calls the * resulting class by the specified name. * * * "T" is the object type */ template class WvList : public WvListBase { // copy constructor: not defined anywhere! WvList(const WvList &list); public: /** Creates an empty linked list. */ WvList() { } /** * Destroys the linked list. * * Destroys any elements that were added with autofree == true. * */ ~WvList() { zap(); } /** Invoked by subclasses after the linked list is first created. */ void setup() {} /** Invoked by subclasses before the linked list is destroyed. */ void shutdown() {} /** * Clears the linked list. * * If destroy is true, destroys any elements that were added with autofree == true. * */ void zap(bool destroy = true) { while (head.next) unlink_after(& head, destroy); } /** * Returns a pointer to the first element in the linked list. * * The list must be non-empty. * * Returns: the element pointer, possibly null */ T *first() const { return (T*)head.next->data; } /** * Returns a pointer to the last element in the linked list. * * The list must be non-empty. * * Returns: the element pointer, possibly null */ T *last() const { return (T*)tail->data; } /** * Adds the element after the specified link in the list. * * "link" is the link preceeding the desired location of the element * to be inserted, non-null * "data" is the element pointer, may be null * "autofree" is if true, takes ownership of the element * "id" is an optional string to associate with the element, or null */ void add_after(WvLink *after, T *data, bool autofree, const char *id = NULL ) { (void)new WvLink((void *)data, after, tail, autofree, id); } /** * Appends the element to the end of the list. * * "data" is the element pointer, may be null * "autofree" is if true, takes ownership of the element * "id" is an optional string to associate with the element, or null */ void append(T *data, bool autofree, const char *id = NULL) { add_after(tail, data, autofree, id); } /** * Synonym for append(T*, bool, char*). * @see append(T*, bool, char*) */ void add(T *data, bool autofree, const char *id = NULL) { append(data, autofree, id); } /** * Prepends the element to the beginning of the list. * * "data" is the element pointer, may be null * "autofree" is if true, takes ownership of the element * "id" is an optional string to associate with the element, or null */ void prepend(T *data, bool autofree, const char *id = NULL) { add_after(&head, data, autofree, id); } /** * Unlinks the specified element from the list. * * Destroys the element if it was added with autofree == true. * * "data" is the element pointer, may be null */ void unlink(T *data) { Iter i(*this); while (i.find(data)) i.unlink(); } /** * Unlinks the first element from the list. * * Destroys the element if it was added with autofree == true. * */ void unlink_first() { if(head.next != NULL) { Iter i(*this); i.rewind(); i.next(); i.unlink(); } } /** * Unlinks the element that follows the specified link in the list. * * Destroys the element if it was added with autofree == true and * destroy == true. * * "after" is the link preceeding the element to be removed, non-null */ void unlink_after(WvLink *after, bool destroy = true) { WvLink *next = after->next; if(next != NULL) { T *obj = (destroy && next->get_autofree()) ? static_cast(next->data) : NULL; if (next == tail) tail = after; next->unlink(after); if (obj) WvTraits::release(obj); } } /** * The iterator type for linked lists. * * An iterator instance does not initially point to any valid * elements in a list. Before using, it must be reset using rewind() * which causes it to point to an imaginary element located before * the first elements in the list. Then next() must be invoked * to incrementally move the iterator along the list to first element, * and then later to the second, third, and subsequent elements. * */ class Iter : public WvListBase::IterBase { public: /** * Binds the iterator to the specified list. * "l" is the list */ Iter(const WvList &l) : IterBase(l) { } /** * Returns a pointer to the current element. * Returns: the element pointer, possibly null */ T *ptr() const { return (T *)link->data; } WvIterStuff(T); /** * Returns the state of autofree for the current element. */ bool get_autofree() const { return link->get_autofree(); } /** * Sets the state of autofree for the current element. */ void set_autofree(bool autofree) { link->set_autofree(autofree); } /** * Unlinks the current element from the list and automatically * increments the iterator to point to the next element as if * next() had been called. */ void unlink(bool destroy = true) { if (prev) ((WvList *)list)->unlink_after(prev, destroy); link = prev->next; } /** * Unlinks the current element from the list but unlike unlink() * automatically returns the iterator to the previous link in * the list such that next() must be called to obtain the * next element. * * This version allows for writing neater loop structures since * an element can be unlinked in mid-traversal while still allowing * the iterator to be incremented at the top of the loop as usual. * * Calling xunlink() twice in a row is currently unsupported. * */ void xunlink(bool destroy = true) { if (prev) ((WvList *)list)->unlink_after(prev, destroy); link = prev; } }; /** The sorted iterator type for linked lists. */ typedef class WvSorter Sorter; }; #define DeclareWvList2(_classname_, _type_) \ typedef class WvList<_type_> _classname_ #define DeclareWvList(_type_) DeclareWvList2(_type_##List, _type_) #endif // __WVLINKLIST_H