Thread overview
Re: Tuples and variable-length template parameter lists
Nov 05, 2012
Simen Kjaeraas
November 05, 2012
Hello all,

Suppose I want to define a tuple type which may have a variable length, e.g.:

	template Tup(ID, Properties...)
	{
		static if(Properties.length == 0)
			alias Tuple!(ID, "id") Tup;
		else
			alias Tuple!(ID, "id", Properties) Tup;
	}

Now, it's trivial to include an arbitrary selection of named values in this, e.g.

	auto t1 = Tup!(size_t, real, "value")(3, 4.5);
	writeln(t1);
	writeln(t1.id, " ", t1.value);

	auto t2 = Tup!(size_t, real, "value", bool, "active")(3, 4.5, true);
	writeln(t2);
	writeln(t2.id, " ", t2.value, " ", t2.active);

However, suppose now I want to define a container struct which holds an array of tuples of the specified type.  Here's what I came up with:

	struct Container(ID, Properties...)
	{
		Tup!(ID, Properties)[] contents;

		void add(ID i, Properties p)
		{
			static if(Properties.length == 0)
				contents ~= Tup!(ID)(i);
			else
				contents ~= Tup!(ID, Properties)(i, p);
		}
	}

Now, if I make properties empty, this works just fine:

	auto c1 = Container!(size_t)();
	c1.add(3);
	c1.add(7);
	c1.add(2);
	writeln(c1);
	foreach(t, tup; c1.contents)
		writeln("[", t, "] ", tup.id);
	writeln();

... and likewise if I pass the container a list of value types without value names:

	auto c2 = Container!(size_t, real, real)();
	c2.add(5, 3.2, 5.6);
	writeln(c2);
	writeln();

... but if I try asking the container to store a tuple with _named_ values, e.g.

	auto c3 = Container!(size_t, real, "value")();

then compilation fails with the following error message:

--------------------------------------------------------------------------------
tupcontainer.d(7): Error: tuple Properties is used as a type
tupcontainer.d(12): Error: template
std.typecons.Tuple!(ulong,"id",real,"value").Tuple.__ctor does not match any
function template declaration
/usr/local/include/d2/std/typecons.d(406): Error: template
std.typecons.Tuple!(ulong,"id",real,"value").Tuple.__ctor cannot deduce template
function from argument types !()(ulong,_error_)
tupcontainer.d(51): Error: template instance
tupcontainer.Container!(ulong,real,"value") error instantiating
--------------------------------------------------------------------------------

I'm confused as to why the Container struct cannot take these template parameters when the corresponding parameters work just fine for Tup.

Can anyone advise what the problem is and if it's possible to get the Container working as envisioned?

Full sample code attached just for clarity. :-)

Thanks and best wishes,

       -- Joe





November 05, 2012
On 2012-11-05, 15:53, Joseph Rushton Wakeling wrote:

> Hello all,
>
> Suppose I want to define a tuple type which may have a variable length, e.g.:
>
> 	template Tup(ID, Properties...)
> 	{
> 		static if(Properties.length == 0)
> 			alias Tuple!(ID, "id") Tup;
> 		else
> 			alias Tuple!(ID, "id", Properties) Tup;
> 	}
>
> Now, it's trivial to include an arbitrary selection of named values in this, e.g.
>
> 	auto t1 = Tup!(size_t, real, "value")(3, 4.5);
> 	writeln(t1);
> 	writeln(t1.id, " ", t1.value);
>
> 	auto t2 = Tup!(size_t, real, "value", bool, "active")(3, 4.5, true);
> 	writeln(t2);
> 	writeln(t2.id, " ", t2.value, " ", t2.active);
>
> However, suppose now I want to define a container struct which holds an array of
> tuples of the specified type.  Here's what I came up with:
>
> 	struct Container(ID, Properties...)
> 	{
> 		Tup!(ID, Properties)[] contents;
>
> 		void add(ID i, Properties p)
> 		{
> 			static if(Properties.length == 0)
> 				contents ~= Tup!(ID)(i);
> 			else
> 				contents ~= Tup!(ID, Properties)(i, p);
> 		}
> 	}
>
> Now, if I make properties empty, this works just fine:
>
> 	auto c1 = Container!(size_t)();
> 	c1.add(3);
> 	c1.add(7);
> 	c1.add(2);
> 	writeln(c1);
> 	foreach(t, tup; c1.contents)
> 		writeln("[", t, "] ", tup.id);
> 	writeln();
>
> ... and likewise if I pass the container a list of value types without value names:
>
> 	auto c2 = Container!(size_t, real, real)();
> 	c2.add(5, 3.2, 5.6);
> 	writeln(c2);
> 	writeln();
>
> ... but if I try asking the container to store a tuple with _named_ values, e.g.
>
> 	auto c3 = Container!(size_t, real, "value")();
>
> then compilation fails with the following error message:
>
> --------------------------------------------------------------------------------
> tupcontainer.d(7): Error: tuple Properties is used as a type
> tupcontainer.d(12): Error: template
> std.typecons.Tuple!(ulong,"id",real,"value").Tuple.__ctor does not match any
> function template declaration
> /usr/local/include/d2/std/typecons.d(406): Error: template
> std.typecons.Tuple!(ulong,"id",real,"value").Tuple.__ctor cannot deduce template
> function from argument types !()(ulong,_error_)
> tupcontainer.d(51): Error: template instance
> tupcontainer.Container!(ulong,real,"value") error instantiating
> --------------------------------------------------------------------------------
>
> I'm confused as to why the Container struct cannot take these template
> parameters when the corresponding parameters work just fine for Tup.
>
> Can anyone advise what the problem is and if it's possible to get the Container
> working as envisioned?

std.typecons.Tuple does a bit of magic behind the scenes. This includes
ridding itself of non-type parameters.

Simply put, you can imagine inserting the type tuple directly into the
function definition:

    void add(ID id, size_t arg0, real arg1, "value" arg2);

as you probably notice, the last argument looks weird.

Now, Phobos does not currently have a staticFilter template, nor does it
have an isType template, so here are implementations of those:


template staticFilter( alias pred, T... ) {
    static if ( T.length == 0 ) {
        alias TypeTuple!( ) staticFilter;
    } else static if ( pred!( T[0] ) ) {
        alias TypeTuple!( T[0], staticFilter!( pred, T[1..$] ) ) staticFilter;
    } else {
        alias staticFilter!( pred, T[1..$] ) staticFilter;
    }
}

unittest {
    static struct S(T...){}

    assert( is( S!(staticFilter!(isType, int, float)) == S!(int, float) ) );
    assert( is( S!(staticFilter!(isType, int, "foo", float)) == S!(int, float) ) );
    assert( is( S!(staticFilter!(isType, "foo", "bar")) == S!() ) );
}

template isType( T... ) if ( T.length == 1 ) {
    enum isType = !is( typeof( T[0] ) );
}

unittest {
    struct S {}
    class C {}

    assert( isType!int );
    assert( isType!string );
    assert( isType!S );
    assert( isType!C );

    assert( !isType!1 );
    assert( !isType!"" );
    assert( !isType!(S( )) );
}


add would then have this signature:

    void add(ID id, staticFilter!(isType, Properties));

-- 
Simen
November 06, 2012
On 11/05/2012 06:14 PM, Simen Kjaeraas wrote:
> std.typecons.Tuple does a bit of magic behind the scenes. This includes
> ridding itself of non-type parameters.
>
> Simply put, you can imagine inserting the type tuple directly into the
> function definition:
>
>      void add(ID id, size_t arg0, real arg1, "value" arg2);
>
> as you probably notice, the last argument looks weird.
>
> Now, Phobos does not currently have a staticFilter template, nor does it
> have an isType template, so here are implementations of those:
>
> add would then have this signature:
>
>      void add(ID id, staticFilter!(isType, Properties));

Oh, very cool!  Thanks ever so much for that.  Incidentally, and I don't understand why, using isTypeTuple also seems to work ...

Is there a case for some patches adding those features to Phobos?