// // Copyright (c) 2000,2001,2002 Brian McNamara and Yannis Smaragdakis // // Permission to use, copy, modify, distribute and sell this software // and its documentation for any purpose is granted without fee, // provided that the above copyright notice and this permission notice // appear in all source code copies and supporting documentation. The // software is provided "as is" without any express or implied // warranty. #ifndef FCPP_LIST_DOT_H #define FCPP_LIST_DOT_H /////////////////////////////////////////////////////////////////////////// // Here we implement (lazy) lists in the List class. There are also a // number of functions associated with lists: // - head, tail, cons, cat, null /////////////////////////////////////////////////////////////////////////// // Order-of-initialization debugging help // Note that you only might need this with the FCPP_1_3_LIST_IMPL version #ifdef FCPP_OOI_DEBUG #include #include #endif #include #include #include #include #include "reuse.h" namespace fcpp { struct fcpp_exception : public std::exception { const char* s; fcpp_exception( const char* ss ) : s(ss) {} const char* what() const throw() { return s; } }; struct Cons; struct Head; struct Tail; struct Null; struct Cat; struct CacheEmpty {}; struct CacheDummy {}; template struct Cache; template struct OddList; template struct ListIterator; template struct ListItHelp; template struct ConsHelp; template struct cvt; template struct ListHelp; template Cache* xempty_helper(); struct ListRaw {}; template class List { IRef > rep; // never NIL, unless an empty OddList template friend class Cache; template friend class OddList; template friend struct ConsHelp; template friend struct cvt; List( const IRef >& p ) : rep(p) {} List( ListRaw, Cache* p ) : rep(p) {} bool priv_isEmpty() const { return rep->cache().second.rep == Cache::XNIL(); } T priv_head() const { #ifdef FCPP_DEBUG if( priv_isEmpty() ) throw fcpp_exception("Tried to take head() of empty List"); #endif return rep->cache().first(); } List priv_tail() const { #ifdef FCPP_DEBUG if( priv_isEmpty() ) throw fcpp_exception("Tried to take tail() of empty List"); #endif return rep->cache().second; } public: typedef T ElementType; List( AUniqueTypeForNil ) : rep( Cache::XEMPTY() ) {} List() : rep( Cache::XEMPTY() ) {} template // works on both ()->OddList and ()->List List( const F& f ) : rep( ListHelp()(f) ) {} // Note: this constructor is still part of List and thus still lazy; // the iterators may not get evaluated until much later. This is a // feature, not a bug. So if the iterators are going to be invalidated // before you finish using the list, then you'd better force evaluation // of the entire list before the iterators go away. template List( const It& begin, const It& end ) : rep( new Cache( ListItHelp(begin,end) ) ) {} List( const OddList& e ) : rep( (e.second.rep != Cache::XNIL()) ? new Cache(e) : Cache::XEMPTY() ) {} #ifdef FCPP_SAFE_LIST // Long lists create long recursions of destructors that blow the // stack. So we have an iterative destructor. It is quite tricky to // get right. The danger is that, when "bypassing" a node to be // unlinked and destructed, that node's 'next' pointer is, in fact, a // List object, whose destructor will be called. As a result, as you // bypass a node, you need to see if its refC is down to 1, and if // so, mutate its next pointer so that when its destructor is called, // it won't cause a recursive cascade. ~List() { while( rep != Cache::XNIL() && rep != Cache::XBAD() ) { if( rep->refC == 1 ) { // This is a rotate(), but this sequence is actually faster // than rotate(), so we do it explicitly IRef > tmp( rep ); rep = rep->val.second.rep; tmp->val.second.rep = Cache::XNIL(); } else rep = rep->val.second.rep; } } #endif operator bool() const { return !priv_isEmpty(); } const OddList& force() const { return rep->cache(); } const List& delay() const { return *this; } operator const OddList&() const { return force(); } T head() const { return priv_head(); } List tail() const { return priv_tail(); } // The following helps makes List almost an STL "container" typedef T value_type; typedef ListIterator const_iterator; typedef const_iterator iterator; // List is immutable iterator begin() const { return ListIterator( *this ); } iterator end() const { return ListIterator(); } }; struct OddListDummyX {}; struct OddListDummyY {}; namespace misc_types { struct Argh { virtual int f() {return 0;} }; typedef int (*PtrToFxn)(); typedef int (Argh::*PtrToMember); typedef int (Argh::*PtrToMemberFxn)(); } template class OddList { // We need to make sure that "fst" is properly aligned to hold a "T" // object, so we do the 'standard' hack. union { unsigned char fst[ sizeof(T) ]; // The real variable // a bunch of dummies of every conceivable type long z1, *pz1; long double z2, *pz2; void *z3, **pz3; misc_types::PtrToFxn z4, *pz4; misc_types::Argh *pz5; int z6, *pz6; char z7, *pz7; double z8, *pz8; misc_types::PtrToMember z9, *pz9; misc_types::PtrToMemberFxn z10, *pz10; }; const T& first() const { return *static_cast(static_cast(&fst)); } T& first() { return *static_cast(static_cast(&fst)); } List second; // If XNIL, then this OddList is NIL template friend class List; template friend class Cache; OddList( OddListDummyX ) : second( Cache::XNIL() ) { } OddList( OddListDummyY ) : second( Cache::XBAD() ) { } void init( const T& x ) { new (static_cast(&fst)) T(x); } bool fst_is_valid() const { if( second.rep != Cache::XNIL() ) if( second.rep != Cache::XBAD() ) return true; return false; } bool priv_isEmpty() const { return second.rep == Cache::XNIL(); } T priv_head() const { #ifdef FCPP_DEBUG if( priv_isEmpty() ) throw fcpp_exception("Tried to take head() of empty OddList"); #endif return first(); } List priv_tail() const { #ifdef FCPP_DEBUG if( priv_isEmpty() ) throw fcpp_exception("Tried to take tail() of empty OddList"); #endif return second; } public: typedef T ElementType; OddList() : second( Cache::XNIL() ) { } OddList( AUniqueTypeForNil ) : second( Cache::XNIL() ) { } OddList( const T& x, const List& y ) : second(y) { init(x); } OddList( const T& x, AUniqueTypeForNil ) : second(Cache::XEMPTY()) { init(x); } OddList( const OddList& x ) : second(x.second) { if( fst_is_valid() ) { init( x.first() ); } } OddList& operator=( const OddList& x ) { if( this == &x ) return *this; if( fst_is_valid() ) { if( x.fst_is_valid() ) first() = x.first(); else first().~T(); } else { if( x.fst_is_valid() ) init( x.first() ); } second = x.second; return *this; } ~OddList() { if( fst_is_valid() ) { first().~T(); } } operator bool() const { return !priv_isEmpty(); } const OddList& force() const { return *this; } List delay() const { return List(*this); } T head() const { return priv_head(); } List tail() const { return priv_tail(); } }; // This converts ()->List to ()->OddList. // In other words, here is the 'extra work' done when using the // unoptimized interface. template struct cvt : public CFunType > { F f; cvt( const F& ff ) : f(ff) {} OddList operator()() const { List l = f(); return l.force(); } }; // I malloc a RefCountType to hold the refCount and init it to 1 to ensure the // refCount will never get to 0, so the destructor-of-global-object // order at the end of the program is a non-issue. In other words, the // memory allocated here is only reclaimed by the operating system. template Cache* xnil_helper() { void *p = std::malloc( sizeof(RefCountType) ); #ifdef FCPP_OOI_DEBUG std::cout << "making a nil/bad:" << typeid(T).name() << " at address " << p << std::endl; #endif *((RefCountType*)p) = 1; return static_cast*>( p ); } template Cache* xempty_helper() { #ifdef FCPP_1_3_LIST_IMPL (void) Cache::xnil; // Make sure xnil exists before moving forward #endif return new Cache( CacheEmpty() ); } template class Cache { RefCountType refC; mutable Fun0 > fxn; mutable OddList val; // val.second.rep can be XBAD, XNIL, or a valid ptr // - XBAD: val is invalid (fxn is valid) // - XNIL: this is the empty list // - anything else: val.first() is head, val.second is tail() // Caches are not copyable or assignable Cache( const Cache& ); void operator=( Cache ); // This functoid should never be called; it represents a // self-referent Cache, which should be impossible under the current // implementation. Nonetheless, we need a 'dummy' function object to // represent invalid 'fxn's (val.second.rep!=XBAD), and this // implementation seems to be among the most reasonable. struct blackhole_helper : CFunType< OddList > { OddList operator()() const { throw fcpp_exception("You have entered a black hole."); } }; #ifdef FCPP_1_3_LIST_IMPL static IRef > xnil, xbad; static IRef > xempty; #endif // Don't get rid of these XFOO() functions; they impose no overhead, // and provide a useful place to add debugging code for tracking down // before-main()-order-of-initialization problems. static const IRef >& XEMPTY() { #ifndef FCPP_1_3_LIST_IMPL static IRef > xempty( xempty_helper() ); #endif #ifdef FCPP_OOI_DEBUG static bool b = true; if(b) { std::cout << "access xempty:" << typeid(T).name() << std::endl; b = false; } #endif return xempty; } static const IRef >& XNIL() { // this list is nil #ifndef FCPP_1_3_LIST_IMPL static IRef > xnil( xnil_helper() ); #endif #ifdef FCPP_OOI_DEBUG static bool b = true; if(b) { std::cout << "access xnil:" << typeid(T).name() << std::endl; b = false; } #endif return xnil; } static const IRef >& XBAD() { // the pair is invalid; use fxn #ifndef FCPP_1_3_LIST_IMPL static IRef > xbad( xnil_helper() ); #endif #ifdef FCPP_OOI_DEBUG static bool b = true; if(b) { std::cout << "access xbad:" << typeid(T).name() << std::endl; b = false; } #endif return xbad; } #ifdef FCPP_1_3_LIST_IMPL static Fun0 > the_blackhole; #endif static Fun0 >& blackhole() { #ifndef FCPP_1_3_LIST_IMPL static Fun0 > the_blackhole( makeFun0( blackhole_helper() ) ); #endif return the_blackhole; } OddList& cache() const { if( val.second.rep == XBAD() ) { val = fxn(); fxn = blackhole(); } return val; } template friend class List; template friend class OddList; template friend struct ConsHelp; template friend struct cvt; template friend struct ListHelp; template friend Cache* xempty_helper(); Cache( CacheEmpty ) : refC(0), fxn(blackhole()), val() {} Cache( const OddList& x ) : refC(0), fxn(blackhole()), val(x) {} Cache( const T& x, const List& l ) : refC(0),fxn(blackhole()),val(x,l) {} Cache( CacheDummy ) : refC(0), fxn(blackhole()), val( OddListDummyX() ) {} Cache( const Fun0 >& f ) : refC(0), fxn(f), val( OddListDummyY() ) {} template Cache( const F& f ) // ()->OddList : refC(0), fxn(makeFun0(f)), val( OddListDummyY() ) {} // This is for ()->List to ()->OddList struct CvtFxn {}; template Cache( CvtFxn, const F& f ) // ()->List : refC(0), fxn(makeFun0(cvt(f))), val( OddListDummyY() ) {} public: void incref() { ++refC; } void decref() { if (!--refC) delete this; } }; #ifdef FCPP_1_3_LIST_IMPL template Fun0 > Cache::the_blackhole( makeFun0( blackhole_helper() ) ); template IRef > Cache::xnil( xnil_helper() ); template IRef > Cache::xbad( xnil_helper() ); template IRef > Cache::xempty( xempty_helper() ); #endif // Rest of List's stuff template struct ListHelp > { IRef > operator()( const F& f ) const { return IRef >(new Cache(Cache::CvtFxn(),f)); } }; template struct ListHelp > { IRef > operator()( const F& f ) const { return IRef >(new Cache(f)); } }; template struct ListItHelp : public CFunType > { It begin, end; ListItHelp( const It& b, const It& e ) : begin(b), end(e) {} OddList operator()() const { if( begin == end ) return NIL; It tmp = begin; T x( *begin ); return cons( x, ListItHelp( ++tmp, end ) ); } }; template #ifdef FCPP_NO_STD_ITER class ListIterator : public std::input_iterator { #else class ListIterator : public std::iterator { #endif List l; bool is_nil; void advance() { l = l.tail(); if( !l ) is_nil = true; } class Proxy { // needed for operator-> const T x; friend class ListIterator; Proxy( const T& xx ) : x(xx) {} public: const T* operator->() const { return &x; } }; public: ListIterator() : l(), is_nil(true) {} explicit ListIterator( const List& ll ) : l(ll), is_nil(!ll) {} const T operator*() const { return l.head(); } const Proxy operator->() const { return Proxy(l.head()); } ListIterator& operator++() { advance(); return *this; } const ListIterator operator++(int) { ListIterator i( *this ); advance(); return i; } bool operator==( const ListIterator& i ) const { return is_nil && i.is_nil; } bool operator!=( const ListIterator& i ) const { return ! this->operator==(i); } }; ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// struct Head { template struct Sig : public FunType {}; template T operator()( const List& l ) const { return l.head(); } template T operator()( const OddList& l ) const { return l.head(); } }; FCPP_MAYBE_NAMESPACE_OPEN FCPP_MAYBE_EXTERN Head head; FCPP_MAYBE_NAMESPACE_CLOSE struct Tail { template struct Sig : public FunType > {}; template List operator()( const List& l ) const { return l.tail(); } template List operator()( const OddList& l ) const { return l.tail(); } }; FCPP_MAYBE_NAMESPACE_OPEN FCPP_MAYBE_EXTERN Tail tail; FCPP_MAYBE_NAMESPACE_CLOSE struct Null { template struct Sig : public FunType {}; template bool operator()( const List& l ) const { return !l; } template bool operator()( const OddList& l ) const { return !l; } }; FCPP_MAYBE_NAMESPACE_OPEN FCPP_MAYBE_EXTERN Null null; FCPP_MAYBE_NAMESPACE_CLOSE template struct ConsHelp > { OddList operator()( const T& x, const F& f ) const { return OddList(x, List( IRef >(new Cache(Cache::CvtFxn(),f)))); } }; template struct ConsHelp > { OddList operator()( const T& x, const F& f ) const { return OddList(x, List( ListRaw(), new Cache(f) )); } }; struct Cons { template struct Sig : public FunType > {}; template OddList operator()( const T& x, const List& l ) const { return OddList(x,l); } template OddList operator()( const T& x, const OddList& l ) const { return OddList(x,l); } template OddList operator()( const T& x, const AUniqueTypeForNil& ) const { return OddList(x,NIL); } template OddList operator()( const T& x, const F& f ) const { return ConsHelp()(x,f); } }; FCPP_MAYBE_NAMESPACE_OPEN FCPP_MAYBE_EXTERN Cons _cons; FCPP_MAYBE_EXTERN Curryable2 cons FCPP_MAYBE_INIT((_cons)); FCPP_MAYBE_NAMESPACE_CLOSE class Cat { // The Intel compiler doesn't like it when I overload this function, // so I just used class template partial specialization in a nested // helper class to code around it. template struct Helper : public CFunType > { OddList operator()( const L& l, const M& m, Reuser2,M> r = NIL ) const { if( null(l) ) return m(); else return cons( head(l), r( *this, tail(l), m ) ); } }; template struct Helper > : public CFunType,OddList > { OddList operator()( const L& l, const List& m, Reuser2,List > r = NIL ) const { if( null(l) ) return m; else return cons( head(l), r( *this, tail(l), m ) ); } }; template struct Helper > : public CFunType,OddList > { OddList operator()( const L& l, const OddList& m, Reuser2,OddList > r = NIL ) const { if( null(l) ) return m; else return cons( head(l), r( *this, tail(l), m ) ); } }; template struct Helper : public CFunType > { OddList operator()( const L& l, const AUniqueTypeForNil& ) const { return l; } }; public: template struct Sig : public FunType > {}; // Note: first arg must be a list, but second arg can be either a list // or a function that returns a list. template OddList operator()( const L& l, const M& m ) const { return Helper()(l,m); } }; FCPP_MAYBE_NAMESPACE_OPEN FCPP_MAYBE_EXTERN Cat _cat; FCPP_MAYBE_EXTERN Curryable2 cat FCPP_MAYBE_INIT((_cat)); FCPP_MAYBE_NAMESPACE_CLOSE struct Delay { template struct Sig : public FunType > {}; template List operator()( const L& l ) const { return l.delay(); } }; FCPP_MAYBE_NAMESPACE_OPEN FCPP_MAYBE_EXTERN Delay delay; FCPP_MAYBE_NAMESPACE_CLOSE struct Force { template struct Sig : public FunType > {}; template OddList operator()( const L& l ) const { return l.force(); } }; FCPP_MAYBE_NAMESPACE_OPEN FCPP_MAYBE_EXTERN Force force; FCPP_MAYBE_NAMESPACE_CLOSE ////////////////////////////////////////////////////////////////////// // op== and op<, overloaded for all combos of List, OddList, and NIL ////////////////////////////////////////////////////////////////////// template bool operator==( const OddList& a, AUniqueTypeForNil ) { return null(a); } template bool operator==( const List& a, AUniqueTypeForNil ) { return null(a); } template bool operator==( AUniqueTypeForNil, const OddList& a ) { return null(a); } template bool operator==( AUniqueTypeForNil, const List& a ) { return null(a); } template bool operator==( const List& a, const List& b ) { if( null(a) && null(b) ) return true; if( null(a) || null(b) ) return false; return (head(a)==head(b)) && (tail(a)==tail(b)); } template bool operator==( const OddList& a, const OddList& b ) { if( null(a) && null(b) ) return true; if( null(a) || null(b) ) return false; return (head(a)==head(b)) && (tail(a)==tail(b)); } template bool operator==( const List& a, const OddList& b ) { if( null(a) && null(b) ) return true; if( null(a) || null(b) ) return false; return (head(a)==head(b)) && (tail(a)==tail(b)); } template bool operator==( const OddList& a, const List& b ) { if( null(a) && null(b) ) return true; if( null(a) || null(b) ) return false; return (head(a)==head(b)) && (tail(a)==tail(b)); } template bool operator<( const List& a, const List& b ) { if( null(a) && !null(b) ) return true; if( null(b) ) return false; if( head(b) < head(a) ) return false; if( head(a) < head(b) ) return true; return (tail(a) < tail(b)); } template bool operator<( const OddList& a, const List& b ) { if( null(a) && !null(b) ) return true; if( null(b) ) return false; if( head(b) < head(a) ) return false; if( head(a) < head(b) ) return true; return (tail(a) < tail(b)); } template bool operator<( const List& a, const OddList& b ) { if( null(a) && !null(b) ) return true; if( null(b) ) return false; if( head(b) < head(a) ) return false; if( head(a) < head(b) ) return true; return (tail(a) < tail(b)); } template bool operator<( const OddList& a, const OddList& b ) { if( null(a) && !null(b) ) return true; if( null(b) ) return false; if( head(b) < head(a) ) return false; if( head(a) < head(b) ) return true; return (tail(a) < tail(b)); } template bool operator<( const OddList&, AUniqueTypeForNil ) { return false; } template bool operator<( const List&, AUniqueTypeForNil ) { return false; } template bool operator<( AUniqueTypeForNil, const OddList& b ) { return !null(b); } template bool operator<( AUniqueTypeForNil, const List& b ) { return !null(b); } ////////////////////////////////////////////////////////////////////// // Handy functions for making list literals ////////////////////////////////////////////////////////////////////// // Yes, these aren't functoids, they're just template functions. I'm // lazy and created these mostly to make it easily to make little lists // in the sample code snippets that appear in papers. template List list_with( const T& a ) { List l; l = cons( a, l ); return l; } template List list_with( const T& a, const T& b ) { List l; l = cons( b, l ); l = cons( a, l ); return l; } template List list_with( const T& a, const T& b, const T& c ) { List l; l = cons( c, l ); l = cons( b, l ); l = cons( a, l ); return l; } template List list_with( const T& a, const T& b, const T& c, const T& d ) { List l; l = cons( d, l ); l = cons( c, l ); l = cons( b, l ); l = cons( a, l ); return l; } template List list_with( const T& a, const T& b, const T& c, const T& d, const T& e ) { List l; l = cons( e, l ); l = cons( d, l ); l = cons( c, l ); l = cons( b, l ); l = cons( a, l ); return l; } } // namespace fcpp #endif