// flatten.C
// ------------------------------------------------------------------------
// Author: Lutz Kettner, ETH Zurich, Switzerland, kettner@inf.ethz.ch
// 23.02.99  presented at the seminar:
// Component-Based Programming under Different Paradigms
// in Dagstuhl, Germany, 21.02.--26.02.1999
//
// This implementation makes use of partial template specialization,
// which is not implemented in every C++ compiler by now. It is
// known to run with Cygnus egcs-2.90.29 980515 (egcs-1.0.3 release).
//
// 'flatten' was used as an example for polytypic programming in 
// functional languages. It raised the question whether a similar 
// program could be written using templates in C++. Besides that 
// the meta-information for the self-inspection of user-defined 
// types must be given explicitly, it can be written.
//
// 'flatten' traverses a DAG (directed acyclic graph) depth-first
// from left to right and collect all items of type 'I' in a list.
// The implementation given here is a template meta-program that
// creates a recursive traversal function for each type involved in 
// the definition of the DAG data structure. The type annotation
// used for self-inspection of the types is encoded in the class
// template T<type,int>.
//
// The output of this program is supposed to be "1 2 3 2 3\n1\n".

#include <iostream.h>
#include <list.h>
#include <algo.h>

// -----------------------------------  USER DATA TYPES
struct B;

struct A {
    int i;
    A* a;
    B* b;
};

struct B {
    A* a;
};

// -----------------------------------  ANNOTATE TYPES FOR SELF-INSPECTION
struct UNKNOWN {};

template <class X, int i> struct T { 
    UNKNOWN operator()( X&) { return UNKNOWN();} 
};

template <> struct T<A,1> {  int  operator()(A& a) { return a.i; } };
template <> struct T<A,2> {  A*   operator()(A& a) { return a.a; } };
template <> struct T<A,3> {  B*   operator()(A& a) { return a.b; } };

template <> struct T<B,1> {  A*   operator()(B& b) { return b.a; } };


// -----------------------------------  LEAF / STRUCT WITH SUBITEMS
template <class Item, class X, int n, class I>
struct flatten_items_class {                     // flatten all subitems
    void operator()( Item i, X x, list<I>& ls) {
        flatten( i, ls);
        flatten_items( T<X,n+1>()(x), T<X,n+1>(), x, ls);
    }
};
template <class Item,int n, class I>
struct flatten_items_class<Item,I,n,I> {         // item of type I found
    void operator()( Item, I i, list<I>& ls) {
        ls.push_back(i);
    }
};
template <int n, class I>                        // item of type I found
struct flatten_items_class<UNKNOWN,I,n,I> {
    void operator()( UNKNOWN, I i, list<I>& ls) {
        ls.push_back(i);
    }
};
template <class X, int n, class I>               // no (further) subitems
struct flatten_items_class<UNKNOWN,X,n,I> {
    void operator()( UNKNOWN, X x, list<I>& ls) {}
};

template <class Item, class X, int n, class I>
void flatten_items( Item i, T<X,n>, X x, list<I>& ls) {
    flatten_items_class<Item,X,n,I>()(i,x,ls);
}

// -----------------------------------  VALUE / POINTER
template <class X, class I>
struct flatten_class {
    void operator()( X x, list<I>& ls) {
        flatten_items( T<X,1>()(x), T<X,1>(), x, ls);
    }
};
template <class P, class I>
struct flatten_class<P*,I> {
    void operator()( P* x, list<I>& ls) { 
        if ( x != NULL)
            flatten_class<P,I>()(*x, ls);
    }
};

template <class X, class I>
void flatten( X x, list<I>& ls) {
    flatten_class<X,I>()( x, ls);
}

// -----------------------------------  EXAMPLE MAIN PROGRAM

int main() {
    A a1; a1.i = 1;
    A a2; a2.i = 2;
    A a3; a3.i = 3;
    B b;
    
    a1.a = &a2;
    a2.a = &a3;
    a3.a = 0;
    a1.b = &b;
    a2.b = 0;
    a3.b = 0;
    b.a = &a2;

    list<int> ls;
    flatten(a1,ls);
    copy( ls.begin(), ls.end(), ostream_iterator<int>(cout, " "));
    cout << endl;

    list<B> ls2;
    flatten(a1,ls2);
    cout << ls2.size();
    cout << endl;
}

// EOF

