get from tuple by type

anonymous via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Mar 15 16:14:31 PDT 2015


On Sunday, 15 March 2015 at 21:59:18 UTC, Charles Cooper wrote:
> C++14 has:
> template<class T, class... Types> constexpr T& 
> get(tuple<Types...>& t);
> Which allows you to get a member of the tuple struct by type. 
> Is there an idiomatic / library way to do this in D? Preferably 
> by indexing.

I don't think there is. I don't know if there should be. 
Distinguishing tuple fields by their type doesn't seem very 
useful to me, since multiple fields can have the same type.

> Here is what I have, it is ugly but works:
> /* CODE */
> static import std.stdio;
> static import std.typecons;
> template GetByType(alias tuple_instance, member_t)
> {
>     ref member_t GetByType() nothrow @nogc @safe {
>         alias tuple_t = typeof(tuple_instance);
>         static assert(std.typecons.isTuple!tuple_t);
>         enum long idx = std.typetuple.staticIndexOf!(member_t, 
> tuple_instance.Types);
>         static if(-1 != idx)
>             return tuple_instance[idx];
>         else static assert(false); //better error message
>     }
> }
>
> static assert(2.5 == GetByType!(std.typecons.tuple(1,2.5), 
> double));
> static assert(2.5 == GetByType!(std.typecons.tuple(1,2.5,3.1), 
> double));
> void main() {
>     auto foo = std.typecons.tuple(1,2.5);
>     std.stdio.writeln(GetByType!(foo, double));
> }
> /* CODE */
>
> Is there a better way to do this?

I went over it (some notes below):

----
import std.typecons: Tuple;

auto ref inout(T) getFirst(T, Types ...)(auto ref 
inout(Tuple!Types) t)
{
     import std.typetuple: staticIndexOf;
     enum idx = staticIndexOf!(T, Types);
     static if(-1 != idx)
         return t[idx];
     else static assert(false); //better error message
}

unittest
{
     import std.typecons: tuple;

     assert(2.5 == tuple(1, 2.5).getFirst!double);
     assert(2.5 == tuple(1, 2.5, 3.1).getFirst!double);
     static assert(2.5 == tuple(1, 2.5).getFirst!double); // CTFE

     static assert(!__traits(compiles, tuple(1, 
2.5).getFirst!string));

     // lvalue tuple => lvalue result
     auto m = tuple(1, 2.5);
     m.getFirst!double = 2.1;
     assert(m[1] == 2.1);

     // rvalue tuple => rvalue result
     static assert(!__traits(compiles, &tuple(1, 
2.5).getFirst!double));

     // immutable/const
     immutable i = tuple(1, 2.5);
     assert(2.5 == i.getFirst!double);
     const c = tuple(1, 2.5);
     assert(2.5 == c.getFirst!double);

}

void main()
{
     import std.stdio: writeln;
     import std.typecons: tuple;
     auto foo = tuple(1, 2.5);
     writeln(foo.getFirst!double);
}
----

Using combined syntax for function template.

Made the tuple a function parameter like in the C++ version. I 
don't see the point in having it a template alias parameter.

Dropped `nothrow @nogc @safe`, since copying the member might not 
be any of that. They are inferred when possible.

Employing inout and `auto ref`.

More tests. unittest block instead of `static assert`s.

Bikeshedding:

Changed name to "getFirst", since subsequent values of the same 
type are ignored.

Named things more like the C++ version: member_t -> T, 
tuple_instance -> t.

Use selective imports instead of static imports.

Use more common casing: types and type templates are PascalCased, 
everything else is camelCased.

Brace placement.


More information about the Digitalmars-d-learn mailing list