From: Hamish Mackenzie (boost_at_[hidden])
Date: 2001-11-26 10:17:16


On Mon, 2001-11-26 at 12:29, Peter Dimov wrote:
> Agreed. We already had this discussion on the list before but I'd like to
> revisit it:
>
> class nil;

I think it will be useful to differentiate between an empty type list
and a nil type. For instance, you can append items to an empty list but
appending to a nil type should probably be an error.

How about type_list::nil?

> Have you considered using std::pair as the 'dot pair' type? It doesn't
> really matter which template is used as long as it has two type parameters -
> it doesn't even need a definition. std::pair is handy since it automatically
> maps a value list (a tuple) to a typelist.

It would mean that in order to tell a pair from a tuple you would have
to use a template class that checks for the empty list type at the end
of the list.

So if we are going to do it I would prefer

template< Head, Tail >
class type_list : private boost::compressed_pair< Head, Rest >
{
  typedef compressed_pair< Head, Tail > base;
public:
  typedef typename base::first_type head_type;
  typedef typename base::second_type tail_type;
  typedef typename base::first_param_type head_param_type;
  typedef typename base::second_param_type tail_param_type;
  typedef typename base::first_reference head_reference;
  typedef typename base::second_reference tail_refenece;
  typedef typename base::first_const_reference head_const_refence;
  typedef typename base::second_const_reference tail_const_refence;

  type_list() : base() {}
  type_list(head_param_type x, tail_param_type y) : base(x, y) {}

  head_reference head() { return base::first(); }
  head_const_reference head() const { return base::first(); }

  tail_reference tail() { return base::second(); }
  tail_const_reference tail() const { return base::second(); }

  void swap( type_list & y ) { base::swap( y ); }

  class nil{};
};

This would simplify using tuples and I can't think of a good argument
not to do it. It would however, mean that tuples and type_lists would be
forever linked. Can anyone think of a reason we might want more than
one tuple type?

The alternative would be

template< class TypeList >
class tuple;

template< class Head, class TypeList >
class tuple< type_list< Head, TypeList > >
  : private pair< Head, TypeList >
{
...
};

Adding storage to type_list would not break this approach, so if we
changed our minds later on it would be no big deal (touch wood).

> Why did you reject the make_typelist<...>::type approach?

template< class X >
class foo;

class a;
class b;
class c;

class foo< typename make_typelist< a, b, c >::type >
{
};

typedef foo< typename make_typelist< a, b, c >::type > x;

As I understand it this is not possible with the existing standard and
is unlikely to every work. The compiler would have to try all possible
instaniations of make_typelist to find one that matches.

On another note how about adding

template< class Head >
type_list< Head, type_list::nil > operator << (
  const type_list::nil &t, const Head &h )
{
  return type_list< Head, type_list::nil >( h, t );
}

template< class Head, class H2, class Tail >
type_list< Head, type_list< H2, Tail > > operator << (
  const type_list< H2, Tail > &t, const Head &h )
{
  return type_list< Head, type_list< H2, Tail > >( h, t );
}

template< class Head, class Tail >
Tail operator >> ( const type_list< Head, Tail > &t, Head &h )
{ // Is there a better way to make this exception safe?
  struct assign_if_not_cancelled
  {
    bool cancelled_;
    Head &dest_;
    const Head &source_;
    
    assign_if_not_cancelled( Head &d, const Head &s )
      : cancelled_( false ), dest_( d ), source_( s ) (
    {
    }
    ~assign_if_not_cancelled()
    {
      if( !cancelled_ )
      {
        dest_ = source_;
      }
    }
  } x( h, t.head() );
  
  try
  {
    return t.tail();
  }
  catch( ... )
  {
    x.cancelled_ = true;
    throw;
  }
}

This could be used as follows

template< class TypeList >
void f( TypeList t )
{
  int a;
  double b;
  char *c;
  
  t >> c >> b >> a;
}

...

f( TYPELIST_0 << 1 << 2.0 << "Test" );

Like streams but LIFO rather than FIFO. We could change it to be FIFO if
that makes more sense.

Hamish