www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - "Modern D Design" :))

reply "Aleksey Bobnev" <uw front.ru> writes:
Yesterday I tried to get back to idea of using D templates for
meta-programming. Last time I tried it there were some serious bugs which
made meta-programming if not impossible at all, still very hard to achieve.

This time it turned out to be a fairly easy task. Though C++ specialization
is still much more powerful than D's one. Therefore template
meta-programming code in C++ is declarative by nature, while in D it seems
to be mostly imperative. But due to unique template syntax introduced in D
and some other advantages, D code surely looks cleaner, much more compact,
and doesn't require any preprocessor macros.

At this time I have only typelists and related meta functions similar to
those from famous Loki library, but it already seems to me - D language has
a Bright(sorry for a pun) future in area of meta-programming. Possibly with
some more improvements to template facility it will beat C++ completely some
day.

I ported a simple lisp program, which finds a solution to hanoi tower
problem, both to C++ and D.
Here's an original code(slightly modified code from xlisp examples):
(defun hanoi(n)
 (eval (cons 'progn (transfer "A" "B" "C" n ))))

(defun print-move ( from to )
  (princ "Move Disk From ")
  (princ from)
  (princ " to ")
  (princ to)
  (princ "\n")
  nil)

(defun transfer ( from to via n )
  (cond ((equal n 1) (list (cons 'print-move (list from to))))
    (t (append
            (append (transfer from via to (- n 1)) (list (cons 'print-move
(list from to))))
            (transfer via to from (- n 1))))))

Here's C++ meta-program "port"(via Loki):
#include <iostream>
#include <typeinfo>
#include "TypeList.h"

using namespace std;

struct A
{
    static const char * name()
    {
        return "A";
    }
};

struct B
{
    static const char * name()
    {
        return "B";
    }
};

struct C
{
    static const char * name()
    {
        return "C";
    }
};

template <typename From,typename To>
struct print_move
{
    static void exec()
    {
        cout<<"Move Disk From "<<From::name()<<" to "<<To::name()<<endl;
    }
};

template <typename From,typename To,typename Via, int n>
struct transfer
{
    typedef typename Loki::TL::Append<typename
transfer<From,Via,To,n-1>::type,Loki::Typelist<print_move<From,To>,Loki::Nul
lType> >::Result type0;
    typedef typename Loki::TL::Append<type0,typename
transfer<Via,To,From,n-1>::type>::Result type;
};

template <typename From,typename To,typename Via>
struct transfer<typename From,typename To,typename Via, 1>
{
    typedef Loki::Typelist<print_move<From,To>,Loki::NullType> type;
};

template <typename FnList>
struct eval
{
};

template <typename Fn,typename Fns>
struct eval <Loki::Typelist<Fn,Fns> >
{
    static void exec()
    {
        Fn::exec();
        eval<Fns>::exec();
    }
};

template <>
struct eval <Loki::NullType>
{
    static void exec()
    {
    }
};

int main()
{
    eval<typename transfer<A,B,C,3>::type>::exec();
}

And here's D port (via my crazy little library):
import Thor.typelist;
import Thor.meta;
import std.stdio;

class A{}
class B{}
class C{}

template PrintMove(From,To)
{
    struct PrintMove
    {
        static void eval()
        {
            writef("Move Disk From %s to
%s\n",typeid(From).toString(),typeid(To).toString());
        }
    }
}

template Transfer(From,To,Via,int n : 1)
{
    alias TL!(PrintMove!(From,To)) Transfer;
}

template Transfer(From,To,Via,int n)
{
    alias
Append!(Append!(.Transfer!(From,Via,To,n-1),TL!(PrintMove!(From,To))),
                  .Transfer!(Via,To,From,n-1)) Transfer;
}

template Eval(TL_ : TypeList)
{
    void Eval()
    {
        TL_.H.eval();
        .Eval!(TL_.T)();
    }
}

template Eval(T : NullT)
{
    void Eval()
    {
    }
}

void main()
{
    Eval!(Transfer!(A,B,C,3))();
}

Now to implementation details. Here is how my TypeList looks and
works(complete code may be found in attachment along with hanoi sample in
C++,
lisp and prolog):

// end of list marker type
class NullT
{
}

// common base type for all typelists
class TypeList
{
    alias NullT H;
    alias NullT T;
    template Length()
    {
        const int Length = 0;
    }
}

// 1-type typelist constructor
template TL(T0)
{
    class TL : TypeList
    {
        public:
        alias T0 H;
        alias NullT T;
        template Length()
        {
            const int Length = 1;
        }
    }
}

// 2-type length typelist
template TL(T0,T1)
{
    class TL : TypeList
    {
        public:
        alias T0 H;
        alias .TL!(T1) T;
        template Length()
        {
            const int Length = 2;
        }
    }
}

// generated constructors for any arbitary number of types
// ...
//

//  Cons is similar to cons in lisp
/*
    Construct type list from head and tail
    typical use :
    Cons!(A,Cons!(B,Cons!(C,NullT)))
*/
template Cons(_Head, _Tail : TypeList)
{
    class Cons : TypeList
    {
        alias _Head H;
        alias _Tail T;
        template Length()
        {
            const int Length = 1 + .Length!(T);
        }
    }
}

template Cons(_Head : NullT, _Tail : TypeList)
{
    class Cons : TypeList
    {
        alias _Tail.H H;
        alias _Tail.T T;
        template Length()
        {
            const int Length = .Length!(_Tail);
        }
    }
}

template Cons(_Head, _Tail : NullT)
{
    class Cons : TypeList
    {
        alias _Head H;
        alias _Tail T;
        template Length()
        {
            const int Length = 1;
        }
    }
}

/*
       General meta-functions
*/

template Equal(T0,T1)
{
    const bool Equal = false;
}

template Equal(T0,T1 : T0)
{
    const bool Equal = true;
}

template SelectType(bool c, T0,T1)
{
    alias T0 SelectType;
}

template SelectType(bool c : false, T0,T1)
{
    alias T1 SelectType;
}

template SelectAlias(bool c, alias T0,alias T1)
{
    alias T0 SelectAlias;
}

template SelectAlias(bool c : false, alias T0, alias T1)
{
    alias T1 SelectAlias;
}

/*
    typelist manipulation
*/
template Length(_TL : TypeList)
{
    const int Length = 1 + .Length!(_TL.T);
}

template Length(_TL : NullT)
{
    const int Length = 0;
}


template RemoveAll(_TL : TypeList, T)
{
    alias SelectType!(Equal!(_TL.H,T),.RemoveAll!(_TL.T,T),
                                      .Cons!(_TL.H,.RemoveAll!(_TL.T,T))
                     ) RemoveAll;
}

template RemoveAll(_TL : NullT, T)
{
    alias NullT RemoveAll;
}

template RemoveAll(_TL, T : NullT)
{
    alias _TL RemoveAll;
}

template Remove(_TL : TypeList, T)
{
    alias SelectType!(Equal!(_TL.H,T),_TL.T,
                                      .Cons!(_TL.H,.Remove!(_TL.T,T))
                     ) Remove;
}

template Remove(_TL : NullT, T)
{
    alias NullT Remove;
}

template Remove(_TL, T : NullT)
{
    alias _TL Remove;
}

template IndexOf(_TL : NullT, T, int i = 0)
{
    const int IndexOf = -1;
}

template IndexOf(_TL : TypeList, T, int i = 0)
{
    const int IndexOf = .Equal!(_TL.H,T) ? i  : .IndexOf!(_TL.T,T,i + 1);
}

template TypeAt(_TL : NullT, int i)
{
    static assert(0);
}

template TypeAt(_TL : TypeList, int i : 0)
{
    alias _TL.H TypeAt;
}

template TypeAt(_TL : TypeList, int i)
{
    alias .TypeAt!(_TL.T,i - 1) TypeAt;
}

template TypeAtNonStrict(_TL : NullT, int i, _Default = NullT)
{
    alias _Default TypeAtNonStrict;
}

template TypeAtNonStrict(_TL : TypeList, int i : 0, _Default = NullT)
{
    alias _TL.H TypeAtNonStrict;
}

template TypeAtNonStrict(_TL : TypeList, int i, _Default = NullT)
{
    alias .TypeAtNonStrict!(_TL.T,i - 1,_Default) TypeAtNonStrict;
}

template Append(_TL0 : TypeList, _TL1: TypeList)
{
    alias Cons!(_TL0.H,.Append!(_TL0.T,_TL1)) Append;
}

template Append(T: NullT, _T : TypeList)
{
    alias _T Append;
}

template Append(T: NullT, _T : NullT)
{
    alias NullT Append;
}

template NoDups(_TL : TypeList)
{
    alias Cons!(_TL.H,Remove!(.NoDups!(_TL.T),_TL.H)) NoDups;
}

template NoDups(_T : NullT)
{
    alias NullT NoDups;
}

template TypeidPrint(_TL : TypeList)
{
    void TypeidPrint()
    {
        typeid(_TL.H).print();
        .TypeidPrint!(_TL.T)();
    }
}

template TypeidPrint(_T)
{
    void TypeidPrint()
    {
        typeid(_T).print();
    }
}

template TypeidPrint(_TL : NullT)
{
    void TypeidPrint()
    {
    }
}

/*
    and finally a little sanity test
*/
alias TL!(int,float,char,double) tl0;
alias TL!(double,char,float,uint) tl1;
alias Append!(tl0,tl1) tl2;
alias NoDups!(tl2) tl3;

template TypeidPrint(_TL : TypeList)
{
    void TypeidPrint()
    {
        typeid(_TL.H).print();
        .TypeidPrint!(_TL.T)();
    }
}

template TypeidPrint(_T)
{
    void TypeidPrint()
    {
        typeid(_T).print();
    }
}

template TypeidPrint(_TL : NullT)
{
    void TypeidPrint()
    {
    }
}

void main()
{
    static assert(IndexOf!(tl0,char) == 2);
    static assert(IndexOf!(tl0,TypeAt!(tl0,IndexOf!(tl0,char))) == 2);
    static
assert(IndexOf!(tl0,TypeAtNonStrict!(tl0,IndexOf!(tl0,uint),char)) == 2);
    printf("tl0.l == %d\ntl1.l == %d\ntl2.l == %d\ntl3.l = %d\n",
            tl0.Length!(),tl1.Length!(),tl2.Length!(),tl3.Length!());
    printf("types in tl0\n");
    TypeidPrint!(tl0)();
    printf("types in tl1\n");
    TypeidPrint!(tl1)();
    printf("types in tl2\n");
    TypeidPrint!(tl2)();
    printf("types in tl3\n");
    TypeidPrint!(tl3)();
}
Dec 27 2004
next sibling parent reply "Walter" <newshound digitalmars.com> writes:
"Aleksey Bobnev" <uw front.ru> wrote in message
news:cqp7v0$1grl$1 digitaldaemon.com...
 This time it turned out to be a fairly easy task. Though C++

 is still much more powerful than D's one. Therefore template
 meta-programming code in C++ is declarative by nature, while in D it seems
 to be mostly imperative. But due to unique template syntax introduced in D
 and some other advantages, D code surely looks cleaner, much more compact,
 and doesn't require any preprocessor macros.

Great!
 At this time I have only typelists and related meta functions similar to
 those from famous Loki library, but it already seems to me - D language

 a Bright(sorry for a pun) future in area of meta-programming. Possibly

 some more improvements to template facility it will beat C++ completely

 day.

What do you think it needs? Also, what in particular are you thinking of by saying that C++ specialization is much more powerful? D's is equivalent, if not more powerful.
Dec 27 2004
parent reply "Ivan Senji" <ivan.senji public.srce.hr> writes:
"Walter" <newshound digitalmars.com> wrote in message
news:cqpgh1$1qrs$1 digitaldaemon.com...
 "Aleksey Bobnev" <uw front.ru> wrote in message
 news:cqp7v0$1grl$1 digitaldaemon.com...

...
. Possibly
 with
 some more improvements to template facility it will beat C++ completely

 day.

What do you think it needs? Also, what in particular are you thinking of

 saying that C++ specialization is much more powerful? D's is equivalent,

 not more powerful.

I know there are people out there that could answer this better than i but i will try: Here is the example of C++ code: <CODE> #include <iostream> using namespace std; template<class U,class V,class X,class Y> class Four { U i; V j; X k; Y l; }; template<class U,class V,class X,class Y> int WhatFour(const Four<U,V,X,Y>& four) { cout << "general template\n"; } template<class U,class V,class X> int WhatFour(const Four<int,U,V,X>& four) { cout << "specialization:: first int\n"; } template<class U,class V> int WhatFour(const Four<U,U,V,V>& four) { cout << "specialization:: first two equal, second two equal\n"; } int main(int argc, char *argv[]) { WhatFour(Four<int,float,char,bool>()); WhatFour(Four<float,float,char,bool>()); WhatFour(Four<float,float,char,char>()); WhatFour(Four<int,int,float,char>()); /* will print: specialization:: first int general template specialization:: first two equal, second two equal specialization:: first int */ return 0; } </CODE> Is there a way to write this in D? If not (and i haven't found one) here is our problem. Specializations like this are used alot in C++.
Dec 30 2004
parent reply "Walter" <newshound digitalmars.com> writes:
"Ivan Senji" <ivan.senji public.srce.hr> wrote in message
news:cr0f07$2uj7$1 digitaldaemon.com...
 Is there a way to write this in D? If not (and i haven't found one) here

 our problem. Specializations like this are used alot in C++.

Ah, I see. What you're asking for is not specialization, but implict instantiation of function templates. You can do the same sort of specialization, however, in D: class Four(U, V, X, Y) { U i; V j; X k; Y l; } template WhatFour(U,V,X,Y) { void func(Four!(U,V,X,Y) four) { printf("general template\n"); } } template WhatFour(U:int,V,X,Y) { void func(Four!(int,V,X,Y) four) { printf("specialization:: first int\n"); } } template WhatFour(U,V:U,X,Y:X) { void func(Four!(U,U,X,X) four) { printf("specialization:: first two equal, second two equal\n"); } } alias WhatFour!(int,float,char,bool).func whatfour; alias WhatFour!(float,float,char,bool).func whatfour; alias WhatFour!(float,float,char,char).func whatfour; alias WhatFour!(int,int,float,char).func whatfour; int main() { Four!(int,float,char,bool) f; Four!(float,float,char,bool) g; Four!(float,float,char,char) h; Four!(int,int,float,char) i; whatfour(f); whatfour(g); whatfour(h); whatfour(i); /* will print: specialization:: first int general template specialization:: first two equal, second two equal specialization:: first int */ return 0; }
Jan 29 2005
parent reply "Ivan Senji" <ivan.senji public.srce.hr> writes:
"Walter" <newshound digitalmars.com> wrote in message
news:cti6lg$1o0q$1 digitaldaemon.com...
 Ah, I see. What you're asking for is not specialization, but implict
 instantiation of function templates. You can do the same sort of
 specialization, however, in D:

 int main()
 {
   Four!(int,float,char,bool) f;
   Four!(float,float,char,bool) g;
   Four!(float,float,char,char) h;
   Four!(int,int,float,char) i;

   whatfour(f);
   whatfour(g);
   whatfour(h);
   whatfour(i);

   /*
   will print:
 specialization:: first int
 general template
 specialization:: first two equal, second two equal
 specialization:: first int
   */

Thanks for this example, but i get: specialization:: first int general template general template specialization:: first int ?
   return 0;
 }

Jan 30 2005
parent "Walter" <newshound digitalmars.com> writes:
"Ivan Senji" <ivan.senji public.srce.hr> wrote in message
news:ctiav5$1u2e$1 digitaldaemon.com...
 Thanks for this example, but i get:

 specialization:: first int
 general template
 general template
 specialization:: first int
 ?

So did I at first. That's a compiler bug, and I fixed it (will appear in next update). -Walter
Jan 30 2005
prev sibling next sibling parent reply "Zz" <Zz Zz.com> writes:
I could not resist.
As a person who really likes Lisp and D here is a Dylan version.

The code is from the recently released Open Dylan.
I personally used Dylan about 3 years ago for a few projects of my own while
not Lisp it has a small runtime and it statically links.

Module:    hanoi
Synopsis:  The classic Towers of Hanoi puzzle
Author:    Andy Armstrong
Copyright:    Original Code is Copyright (c) 1995-2004 Functional Objects,
Inc.
              All rights reserved.
License:      Functional Objects Library Public License Version 1.0
Dual-license: GNU Lesser General Public License
Warranty:     Distributed WITHOUT WARRANTY OF ANY KIND

define class <disk> (<object>)
  constant slot diameter :: <integer>,
    required-init-keyword: diameter:;
end class <disk>;

define function make-disk
    (integer :: <integer>) => (disk :: <disk>)
  make(<disk>, diameter: integer)
end function make-disk;

define class <tower> (<object>)
  constant slot name :: <string>,
    required-init-keyword: name:;
  constant slot disks :: <deque> = make(<deque>);
end class <tower>;

define method initialize
    (tower :: <tower>,
     #key initial-disks :: <sequence> = #[])
 => ()
  next-method();
  for (disk in initial-disks)
    push(tower.disks, disk)
  end
end method initialize;

define method height
    (tower :: <tower>) => (height :: <integer>)
  size(tower.disks)
end method height;

define variable *n-operations* :: <integer> = 0;

define method move-disk
    (from-tower :: <tower>, to-tower :: <tower>)
  *n-operations* := *n-operations* + 1;
  format-out(".");
  let disk = pop(from-tower.disks);
  push(to-tower.disks, disk)
end method move-disk;

define method hanoi
    (from-tower :: <tower>, to-tower :: <tower>, with-tower :: <tower>,
     #key count :: <integer> = from-tower.height)
 => ()
  if (count >= 1)
    hanoi(from-tower, with-tower, to-tower, count: count - 1);
    move-disk(from-tower, to-tower);
    hanoi(with-tower, to-tower, from-tower, count: count - 1)
  end
end method hanoi;
Dec 27 2004
parent Norbert Nemec <Norbert Nemec-online.de> writes:
Zz wrote:

 The code is from the recently released Open Dylan.

I know this is drifting off-topic, but still: what is "Open Dylan"? I know of Gwydion and FunctionalDeveloper. The news page of Gwydion mentiones that FD might become OpenDylan one day, but information generally seems rather confusing.
Jan 07 2005
prev sibling parent M <M_member pathlink.com> writes:
Think the Lisp wins this on ;)

In article <cqp7v0$1grl$1 digitaldaemon.com>, Aleksey Bobnev says...
Yesterday I tried to get back to idea of using D templates for
meta-programming. Last time I tried it there were some serious bugs which
made meta-programming if not impossible at all, still very hard to achieve.

This time it turned out to be a fairly easy task. Though C++ specialization
is still much more powerful than D's one. Therefore template
meta-programming code in C++ is declarative by nature, while in D it seems
to be mostly imperative. But due to unique template syntax introduced in D
and some other advantages, D code surely looks cleaner, much more compact,
and doesn't require any preprocessor macros.

At this time I have only typelists and related meta functions similar to
those from famous Loki library, but it already seems to me - D language has
a Bright(sorry for a pun) future in area of meta-programming. Possibly with
some more improvements to template facility it will beat C++ completely some
day.

I ported a simple lisp program, which finds a solution to hanoi tower
problem, both to C++ and D.
Here's an original code(slightly modified code from xlisp examples):
(defun hanoi(n)
 (eval (cons 'progn (transfer "A" "B" "C" n ))))

(defun print-move ( from to )
  (princ "Move Disk From ")
  (princ from)
  (princ " to ")
  (princ to)
  (princ "\n")
  nil)

(defun transfer ( from to via n )
  (cond ((equal n 1) (list (cons 'print-move (list from to))))
    (t (append
            (append (transfer from via to (- n 1)) (list (cons 'print-move
(list from to))))
            (transfer via to from (- n 1))))))

Here's C++ meta-program "port"(via Loki):
#include <iostream>
#include <typeinfo>
#include "TypeList.h"

using namespace std;

struct A
{
    static const char * name()
    {
        return "A";
    }
};

struct B
{
    static const char * name()
    {
        return "B";
    }
};

struct C
{
    static const char * name()
    {
        return "C";
    }
};

template <typename From,typename To>
struct print_move
{
    static void exec()
    {
        cout<<"Move Disk From "<<From::name()<<" to "<<To::name()<<endl;
    }
};

template <typename From,typename To,typename Via, int n>
struct transfer
{
    typedef typename Loki::TL::Append<typename
transfer<From,Via,To,n-1>::type,Loki::Typelist<print_move<From,To>,Loki::Nul
lType> >::Result type0;
    typedef typename Loki::TL::Append<type0,typename
transfer<Via,To,From,n-1>::type>::Result type;
};

template <typename From,typename To,typename Via>
struct transfer<typename From,typename To,typename Via, 1>
{
    typedef Loki::Typelist<print_move<From,To>,Loki::NullType> type;
};

template <typename FnList>
struct eval
{
};

template <typename Fn,typename Fns>
struct eval <Loki::Typelist<Fn,Fns> >
{
    static void exec()
    {
        Fn::exec();
        eval<Fns>::exec();
    }
};

template <>
struct eval <Loki::NullType>
{
    static void exec()
    {
    }
};

int main()
{
    eval<typename transfer<A,B,C,3>::type>::exec();
}

And here's D port (via my crazy little library):
import Thor.typelist;
import Thor.meta;
import std.stdio;

class A{}
class B{}
class C{}

template PrintMove(From,To)
{
    struct PrintMove
    {
        static void eval()
        {
            writef("Move Disk From %s to
%s\n",typeid(From).toString(),typeid(To).toString());
        }
    }
}

template Transfer(From,To,Via,int n : 1)
{
    alias TL!(PrintMove!(From,To)) Transfer;
}

template Transfer(From,To,Via,int n)
{
    alias
Append!(Append!(.Transfer!(From,Via,To,n-1),TL!(PrintMove!(From,To))),
                  .Transfer!(Via,To,From,n-1)) Transfer;
}

template Eval(TL_ : TypeList)
{
    void Eval()
    {
        TL_.H.eval();
        .Eval!(TL_.T)();
    }
}

template Eval(T : NullT)
{
    void Eval()
    {
    }
}

void main()
{
    Eval!(Transfer!(A,B,C,3))();
}

Now to implementation details. Here is how my TypeList looks and
works(complete code may be found in attachment along with hanoi sample in
C++,
lisp and prolog):

// end of list marker type
class NullT
{
}

// common base type for all typelists
class TypeList
{
    alias NullT H;
    alias NullT T;
    template Length()
    {
        const int Length = 0;
    }
}

// 1-type typelist constructor
template TL(T0)
{
    class TL : TypeList
    {
        public:
        alias T0 H;
        alias NullT T;
        template Length()
        {
            const int Length = 1;
        }
    }
}

// 2-type length typelist
template TL(T0,T1)
{
    class TL : TypeList
    {
        public:
        alias T0 H;
        alias .TL!(T1) T;
        template Length()
        {
            const int Length = 2;
        }
    }
}

// generated constructors for any arbitary number of types
// ...
//

//  Cons is similar to cons in lisp
/*
    Construct type list from head and tail
    typical use :
    Cons!(A,Cons!(B,Cons!(C,NullT)))
*/
template Cons(_Head, _Tail : TypeList)
{
    class Cons : TypeList
    {
        alias _Head H;
        alias _Tail T;
        template Length()
        {
            const int Length = 1 + .Length!(T);
        }
    }
}

template Cons(_Head : NullT, _Tail : TypeList)
{
    class Cons : TypeList
    {
        alias _Tail.H H;
        alias _Tail.T T;
        template Length()
        {
            const int Length = .Length!(_Tail);
        }
    }
}

template Cons(_Head, _Tail : NullT)
{
    class Cons : TypeList
    {
        alias _Head H;
        alias _Tail T;
        template Length()
        {
            const int Length = 1;
        }
    }
}

/*
       General meta-functions
*/

template Equal(T0,T1)
{
    const bool Equal = false;
}

template Equal(T0,T1 : T0)
{
    const bool Equal = true;
}

template SelectType(bool c, T0,T1)
{
    alias T0 SelectType;
}

template SelectType(bool c : false, T0,T1)
{
    alias T1 SelectType;
}

template SelectAlias(bool c, alias T0,alias T1)
{
    alias T0 SelectAlias;
}

template SelectAlias(bool c : false, alias T0, alias T1)
{
    alias T1 SelectAlias;
}

/*
    typelist manipulation
*/
template Length(_TL : TypeList)
{
    const int Length = 1 + .Length!(_TL.T);
}

template Length(_TL : NullT)
{
    const int Length = 0;
}


template RemoveAll(_TL : TypeList, T)
{
    alias SelectType!(Equal!(_TL.H,T),.RemoveAll!(_TL.T,T),
                                      .Cons!(_TL.H,.RemoveAll!(_TL.T,T))
                     ) RemoveAll;
}

template RemoveAll(_TL : NullT, T)
{
    alias NullT RemoveAll;
}

template RemoveAll(_TL, T : NullT)
{
    alias _TL RemoveAll;
}

template Remove(_TL : TypeList, T)
{
    alias SelectType!(Equal!(_TL.H,T),_TL.T,
                                      .Cons!(_TL.H,.Remove!(_TL.T,T))
                     ) Remove;
}

template Remove(_TL : NullT, T)
{
    alias NullT Remove;
}

template Remove(_TL, T : NullT)
{
    alias _TL Remove;
}

template IndexOf(_TL : NullT, T, int i = 0)
{
    const int IndexOf = -1;
}

template IndexOf(_TL : TypeList, T, int i = 0)
{
    const int IndexOf = .Equal!(_TL.H,T) ? i  : .IndexOf!(_TL.T,T,i + 1);
}

template TypeAt(_TL : NullT, int i)
{
    static assert(0);
}

template TypeAt(_TL : TypeList, int i : 0)
{
    alias _TL.H TypeAt;
}

template TypeAt(_TL : TypeList, int i)
{
    alias .TypeAt!(_TL.T,i - 1) TypeAt;
}

template TypeAtNonStrict(_TL : NullT, int i, _Default = NullT)
{
    alias _Default TypeAtNonStrict;
}

template TypeAtNonStrict(_TL : TypeList, int i : 0, _Default = NullT)
{
    alias _TL.H TypeAtNonStrict;
}

template TypeAtNonStrict(_TL : TypeList, int i, _Default = NullT)
{
    alias .TypeAtNonStrict!(_TL.T,i - 1,_Default) TypeAtNonStrict;
}

template Append(_TL0 : TypeList, _TL1: TypeList)
{
    alias Cons!(_TL0.H,.Append!(_TL0.T,_TL1)) Append;
}

template Append(T: NullT, _T : TypeList)
{
    alias _T Append;
}

template Append(T: NullT, _T : NullT)
{
    alias NullT Append;
}

template NoDups(_TL : TypeList)
{
    alias Cons!(_TL.H,Remove!(.NoDups!(_TL.T),_TL.H)) NoDups;
}

template NoDups(_T : NullT)
{
    alias NullT NoDups;
}

template TypeidPrint(_TL : TypeList)
{
    void TypeidPrint()
    {
        typeid(_TL.H).print();
        .TypeidPrint!(_TL.T)();
    }
}

template TypeidPrint(_T)
{
    void TypeidPrint()
    {
        typeid(_T).print();
    }
}

template TypeidPrint(_TL : NullT)
{
    void TypeidPrint()
    {
    }
}

/*
    and finally a little sanity test
*/
alias TL!(int,float,char,double) tl0;
alias TL!(double,char,float,uint) tl1;
alias Append!(tl0,tl1) tl2;
alias NoDups!(tl2) tl3;

template TypeidPrint(_TL : TypeList)
{
    void TypeidPrint()
    {
        typeid(_TL.H).print();
        .TypeidPrint!(_TL.T)();
    }
}

template TypeidPrint(_T)
{
    void TypeidPrint()
    {
        typeid(_T).print();
    }
}

template TypeidPrint(_TL : NullT)
{
    void TypeidPrint()
    {
    }
}

void main()
{
    static assert(IndexOf!(tl0,char) == 2);
    static assert(IndexOf!(tl0,TypeAt!(tl0,IndexOf!(tl0,char))) == 2);
    static
assert(IndexOf!(tl0,TypeAtNonStrict!(tl0,IndexOf!(tl0,uint),char)) == 2);
    printf("tl0.l == %d\ntl1.l == %d\ntl2.l == %d\ntl3.l = %d\n",
            tl0.Length!(),tl1.Length!(),tl2.Length!(),tl3.Length!());
    printf("types in tl0\n");
    TypeidPrint!(tl0)();
    printf("types in tl1\n");
    TypeidPrint!(tl1)();
    printf("types in tl2\n");
    TypeidPrint!(tl2)();
    printf("types in tl3\n");
    TypeidPrint!(tl3)();
}


begin 666 Thor.zip
M4$L#! H``````)V.FS$````````````````$````8W!P+U!+`P04````" ":
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
M````  0``&-P<"]4>7!E3&ES="YH4$L!`A0`% ```` `R+K4*IFQI(4\! ``
M;A ```\```````````` ````W!(``&-P<"]4>7!E36%N:7 N:%!+`0(4`!0`
M```(`/>$FS$ACR9840$``$(#```'````````````( ```$49``!H86YO:2YD
M4$L!`A0`% ```` `+;^8,3,.J<'-````XP$```D```````````` ````NQH`
M`&AA;F]I+FQS<%!+`0(4`!0````(`$:^F#%?U[5G`0$``/H!```(````````
M````( ```*\;``!H86YO:2YP;%!+`0(4``H``````(N.FS$`````````````
M```%````````````$ ```-8<``!4:&]R+U!+`0(4`!0````(`#R5F3%W%P8W
M6 $``"8#```+````````````( ```/D<``!4:&]R+VUE=&$N9%!+`0(4`!0`
M```(`"J/FS$H6*JGAP(``! '```-````````````( ```'P>``!4:&]R+W1R
M86ET<RYD4$L!`A0`% ```` `Y**9,0XG:P*K! ``Z",```\```````````` 
L````+B$``%1H;W(O='EP96QI<W0N9%!+!08`````# `,`*L"```&) ``````
`
end

Dec 28 2004