Jump to page: 1 2
Thread overview
Places a TypeTuple can be used
Aug 16, 2013
Ali Çehreli
Aug 16, 2013
Meta
Aug 16, 2013
Ali Çehreli
Aug 16, 2013
Jonathan M Davis
Aug 16, 2013
monarch_dodra
Aug 16, 2013
Jonathan M Davis
Aug 16, 2013
Timon Gehr
Aug 16, 2013
Timon Gehr
Aug 16, 2013
Ali Çehreli
Aug 16, 2013
Dicebot
Aug 16, 2013
Ali Çehreli
Aug 16, 2013
John Colvin
Aug 16, 2013
Dicebot
August 16, 2013
I know of three places a TypeTuple can be used at:

1) Function parameter list

2) Template parameter list

3) Array literal element list

Are there more?

import std.typetuple;

void foo(int, string, double)
{}

struct S(T, float f)
{}

void main()
{
    // 1) Function parameter list: May not contain types
    foo(TypeTuple!(42, "hello", 1.5));

    // 2) Template parameter list: May contain types
    auto s = S!(TypeTuple!(char, 2.5))();

    // 3) Array elements: Elements must be the same type
    auto a = [ TypeTuple!(1, 2, 3, 4) ];

    // Are there other places that a TypeTuple can be used?
}

Ali
August 16, 2013
On Friday, 16 August 2013 at 04:14:04 UTC, Ali Çehreli wrote:
> I know of three places a TypeTuple can be used at:
>
> 1) Function parameter list
>
> 2) Template parameter list
>
> 3) Array literal element list
>
> Are there more?
>
> import std.typetuple;
>
> void foo(int, string, double)
> {}
>
> struct S(T, float f)
> {}
>
> void main()
> {
>     // 1) Function parameter list: May not contain types
>     foo(TypeTuple!(42, "hello", 1.5));
>
>     // 2) Template parameter list: May contain types
>     auto s = S!(TypeTuple!(char, 2.5))();
>
>     // 3) Array elements: Elements must be the same type
>     auto a = [ TypeTuple!(1, 2, 3, 4) ];
>
>     // Are there other places that a TypeTuple can be used?
> }
>
> Ali

I'm not sure if it should be considered a separate case, but there is this:

foreach(type; TypeTuple!(ubyte, uint, ulong))
{
    writeln(type.max);
}
August 16, 2013
On 08/15/2013 10:39 PM, Meta wrote:

> On Friday, 16 August 2013 at 04:14:04 UTC, Ali Çehreli wrote:

>> 1) Function parameter list
>>
>> 2) Template parameter list
>>
>> 3) Array literal element list
>>
>> Are there more?

> I'm not sure if it should be considered a separate case, but there is this:
>
> foreach(type; TypeTuple!(ubyte, uint, ulong))
> {
>      writeln(type.max);
> }

Thanks! That's a very interesting one. Other than the peculiarity of the loop being a compile-time foreach, that TypeTuple is appearing at a place where its expansion is not even valid:

    foreach (type; ubyte, uint, ulong) {    // compilation error

Ali

August 16, 2013
On Thursday, August 15, 2013 23:09:55 Ali Çehreli wrote:
> On 08/15/2013 10:39 PM, Meta wrote:
>  > On Friday, 16 August 2013 at 04:14:04 UTC, Ali Çehreli wrote:
>  >> 1) Function parameter list
>  >>
>  >> 2) Template parameter list
>  >>
>  >> 3) Array literal element list
>  >>
>  >> Are there more?
>  >
>  > I'm not sure if it should be considered a separate case, but there is
> 
> this:
>  > foreach(type; TypeTuple!(ubyte, uint, ulong))
>  > {
>  >
>  >      writeln(type.max);
>  >
>  > }
> 
> Thanks! That's a very interesting one. Other than the peculiarity of the loop being a compile-time foreach, that TypeTuple is appearing at a place where its expansion is not even valid:
> 
>      foreach (type; ubyte, uint, ulong) {    // compilation error

That's because what you just wrote is not equivalent to what TypeTuple is. TypeTuple is an alias for a parameter or argument list, not simply a list of types or values. I don't know of a way to write the equivalent in a foreach loop without TypeTuple or some other alias which was the same as TypeTuple.

- Jonathan M Davis
August 16, 2013
On Thursday, August 15, 2013 21:14:03 Ali Çehreli wrote:
> I know of three places a TypeTuple can be used at:
> 
> 1) Function parameter list
> 
> 2) Template parameter list
> 
> 3) Array literal element list
> 
> Are there more?
> 
> import std.typetuple;
> 
> void foo(int, string, double)
> {}
> 
> struct S(T, float f)
> {}
> 
> void main()
> {
>      // 1) Function parameter list: May not contain types
>      foo(TypeTuple!(42, "hello", 1.5));
> 
>      // 2) Template parameter list: May contain types
>      auto s = S!(TypeTuple!(char, 2.5))();
> 
>      // 3) Array elements: Elements must be the same type
>      auto a = [ TypeTuple!(1, 2, 3, 4) ];
> 
>      // Are there other places that a TypeTuple can be used?
> }

It can be used in foreach, which essentially makes it a static foreach. This is extremely useful when unit testing. e.g.

foreach(T; TypeTuple!(char[], wchar[], dchar[], string, wstring, dstring))
{
    assert(stripLeft(to!T("  foo  ")) == to!T("foo  "));
}

The code within the foreach is duplicated, because the loop is run at compile time. So, the above code becomes something like

{assert(stripLeft(to!(char[])("  foo  ")) == to!(char[])("foo  "));}
{assert(stripLeft(to!(wchar[])("  foo  ")) == to!(wchar[])("foo  "));}
{assert(stripLeft(to!(dchar[])("  foo  ")) == to!(dchar[])("foo  "));}
{assert(stripLeft(to!string("  foo  ")) == to!string("foo  "));}
{assert(stripLeft(to!wstring("  foo  ")) == to!wstring("foo  "));}
{assert(stripLeft(to!dstring("  foo  ")) == to!dstring("foo  "));}

You can use TypeTuple in enums, though the enum value would have to be used in a context that TypeTuples can be used in. You can also alias them.

There are probably others, especially when you start messing with templates and parameter lists, but while those use the built-in tuples which TypeTuple aliases, I can't think of any case at the moment where you would explicitly use TypeTuple.

You should probably read this article if you haven't:

http://dlang.org/tuple.html

Unfortunately, it muddles things a fair bit, because it uses Tuple instead of std.typetuple.TypeTuple, and then it talks about expression tuples vs type tuples (whereas a TypeTuple could be either an expression tuple or a type tuple), but that just highlights how confusing this is and how badly named TypeTuple is (but unfortunately, it's used in so much code now, that renaming it just isn't going to happen). That article should probably be rewritten so that it takes TypeTuple into account. But it does contain some good info if you can properly understand the difference between TypeTuple and a type tuple.

- Jonathan M Davis
August 16, 2013
On Friday, 16 August 2013 at 05:39:32 UTC, Meta wrote:
> On Friday, 16 August 2013 at 04:14:04 UTC, Ali Çehreli wrote:
>> I know of three places a TypeTuple can be used at:
>>
>> 1) Function parameter list
>>
>> 2) Template parameter list
>>
>> 3) Array literal element list
>>
>> Are there more?
>>
>> import std.typetuple;
>>
>> void foo(int, string, double)
>> {}
>>
>> struct S(T, float f)
>> {}
>>
>> void main()
>> {
>>    // 1) Function parameter list: May not contain types
>>    foo(TypeTuple!(42, "hello", 1.5));
>>
>>    // 2) Template parameter list: May contain types
>>    auto s = S!(TypeTuple!(char, 2.5))();
>>
>>    // 3) Array elements: Elements must be the same type
>>    auto a = [ TypeTuple!(1, 2, 3, 4) ];
>>
>>    // Are there other places that a TypeTuple can be used?
>> }
>>
>> Ali
>
> I'm not sure if it should be considered a separate case, but there is this:
>
> foreach(type; TypeTuple!(ubyte, uint, ulong))
> {
>     writeln(type.max);
> }

This works for values too btw:

This creates an explicitly unrolled loop, for example.
a = 1;
foreach(type; TypeTuple!(1, 2, 3))
    a *= i;

Or, extracted and simplified from UTF:
private dchar decodeImpl(auto ref S str, ref size_t index)
{
    assert(fst & 0x80);
    ubyte tmp = void;
    dchar d = fst; // upper control bits are masked out later
    fst <<= 1;

    foreach (i; TypeTuple!(1, 2, 3))
    {
        tmp = pstr[i];

        if ((tmp & 0xC0) != 0x80)
            throw invalidUTF();

        d = (d << 6) | (tmp & 0x3F);
        fst <<= 1;

        if (!(fst & 0x80)) // no more bytes
        {
            d &= bitMask[i]; // mask out control bits

            // overlong, could have been encoded with i bytes
            if ((d & ~bitMask[i - 1]) == 0)
                throw invalidUTF();

            // check for surrogates only needed for 3 bytes
            static if (i == 2)
            {
                if (!isValidDchar(d))
                    throw invalidUTF();
            }

            index += i + 1;
            return d;
        }
    }

    throw invalidUTF();
}

Here, notice that the loop is repeated 3 times, but loop number 2 is statically different from the other loops, by using a "static if (i == 2)"
August 16, 2013
On 08/16/2013 06:14 AM, Ali Çehreli wrote:
> I know of three places a TypeTuple can be used at:
>
> 1) Function parameter list
>
> 2) Template parameter list
>

In fact, no.

template A(Seq!(int,int) a){ enum A=a[0]; } // error

> 3) Array literal element list
>
> Are there more?
>...

I assume you mean just the contexts where it auto-expands in some way?


- Declared type of a tuple of variables (or function parameters):

Seq!(int,int) x;
assert(is(typeof(x[0])==int)&&is(typeof(x[1])==int));


void foo(Seq!(int,int) x);


- Function argument list (including struct constructor calls, excluding arguments to overloaded operators):

foo(Seq!(1,2));


- Index argument list:

a[Seq!(1)]


- new argument list(s):

new A(Seq!(1,2));

// new!(Seq!(1,2)) A(Seq!(1,2)); // to be deprecated


- Parent list:

class A: Seq!(B,C){ }


- Array literal element list (for completeness of this list):

[Seq!(1,2)]


I think those might be all usages accepted by DMD.

IMO it should work everywhere a comma-separated list of expressions/types is expected.
Some arbitrary limitations implemented by DMD:

- Arguments to (static) assert statement:

assert(Seq!(false, "boom!")); // error

- Arguments to mixin statements:

mixin(Seq!"int x=2;"); // error

- Arguments to overloaded operators:

s.opBinary!"+"(Seq!(s,s)); // ok
s+Seq!(s,s); // error

- Case lists:

switch(1){
    case Seq!(1,2): break; // error
    default: break;
}

- template parameter lists

template A(Seq!(int,int) a){ enum A=a[0]; } // error

- Second argument to comma expression in valid context:

foo((x++,Seq!(1,2))); // error

August 16, 2013
On 08/16/2013 01:09 PM, Timon Gehr wrote:
>
> - Arguments to string mixins:
>
> mixin(Seq!"int x=2;"); // error

fixed.
August 16, 2013
On Friday, 16 August 2013 at 04:14:04 UTC, Ali Çehreli wrote:
> ...

dlang.org documentation on this topic is quite confusing, unfortunately. There was a small discussion in this http://d.puremagic.com/issues/show_bug.cgi?id=10803 bug report recently where Kenji has surprised me a lot with explanation how this really is intended to work.

August 16, 2013
On 08/16/2013 04:09 AM, Timon Gehr wrote:
> IMO it should work everywhere a comma-separated list of
> expressions/types is expected.

Exactly. Only then this concept would be easy to understand and explain. It would make "a comma-separated list of expressions/types" a language construct. Done.

Thank you everyone. Great information! I am going over the my tuples chapter where TypeTuple appeared briefly with just a unittest example. I am expanding the information there but I will include only the common uses of it.

Ali

« First   ‹ Prev
1 2