Jump to page: 1 2
Thread overview
get from tuple by type
Mar 15, 2015
Charles Cooper
Mar 15, 2015
ketmar
Mar 15, 2015
weaselcat
Mar 15, 2015
ketmar
Mar 15, 2015
ketmar
Mar 15, 2015
Charles Cooper
Mar 15, 2015
ketmar
Mar 15, 2015
Charles Cooper
Mar 15, 2015
ketmar
Mar 15, 2015
bearophile
Mar 15, 2015
Charles Cooper
Mar 15, 2015
anonymous
Mar 15, 2015
Charles Cooper
Mar 15, 2015
bearophile
Mar 15, 2015
Charles Cooper
Mar 15, 2015
anonymous
Mar 15, 2015
Charles Cooper
March 15, 2015
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.

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?
March 15, 2015
On Sun, 15 Mar 2015 21:59:16 +0000, 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.

why indexing? functional programming is fun!

  import std.typecons : tuple;

  template GetByType(Type, Tuple...) {
    import std.typecons : isTuple;
    static if (Tuple.length == 0)
      static assert(false, Type.stringof~" not found");
    else static if (Tuple.length == 1 && isTuple!(typeof(Tuple[0])))
      enum GetByType = GetByType!(Type, Tuple[0].expand);
    else static if (is(typeof(Tuple[0]) == Type))
      enum GetByType = Tuple[0];
    else
      enum GetByType = GetByType!(Type, Tuple[1..$]);
  }

  static assert(2.5 == GetByType!(double, 1, 2.5));
  static assert(2.5 == GetByType!(double, 1, 2.5, 3.1));
  //static assert(2.5 == GetByType!(char, 1, 2.5, 3.1));
  static assert(2.5 == GetByType!(double, std.typecons.tuple(1, 2.5)));
  static assert(2.5 == GetByType!(double, std.typecons.tuple(1,2.5,3.1)));


March 15, 2015
Charles Cooper:

> Is there a better way to do this?

Can you show some use cases for this, and isn't "foo[1]" better?

Bye,
bearophile
March 15, 2015
On Sunday, 15 March 2015 at 22:21:04 UTC, ketmar wrote:
> On Sun, 15 Mar 2015 21:59:16 +0000, 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.
>
> why indexing? functional programming is fun!
>
>   import std.typecons : tuple;
>
>   template GetByType(Type, Tuple...) {
>     import std.typecons : isTuple;
>     static if (Tuple.length == 0)
>       static assert(false, Type.stringof~" not found");
>     else static if (Tuple.length == 1 && isTuple!(typeof(Tuple[0])))
>       enum GetByType = GetByType!(Type, Tuple[0].expand);
>     else static if (is(typeof(Tuple[0]) == Type))
>       enum GetByType = Tuple[0];
>     else
>       enum GetByType = GetByType!(Type, Tuple[1..$]);
>   }
>
>   static assert(2.5 == GetByType!(double, 1, 2.5));
>   static assert(2.5 == GetByType!(double, 1, 2.5, 3.1));
>   //static assert(2.5 == GetByType!(char, 1, 2.5, 3.1));
>   static assert(2.5 == GetByType!(double, std.typecons.tuple(1, 2.5)));
>   static assert(2.5 == GetByType!(double, std.typecons.tuple(1,2.5,3.1)));

Seems like a useful feature(useful enough to get past the C++ standards committee,) consider submitting a phobos PR?
March 15, 2015
On Sun, 15 Mar 2015 22:35:03 +0000, weaselcat wrote:

> Seems like a useful feature(useful enough to get past the C++ standards committee,) consider submitting a phobos PR?

ah, c++ committee accepts by randomness. ;-) honestly, i can't see why it's useful and where it can be used. and it's so easy to write anyway, so i think that there is virtually no sense in adding something like this to Phobos.

March 15, 2015
p.s. to be clear: it's freaking hard to do metaprogramming and template functional programming in c++, that's why c++ committee accepts such things. and it's very easy to write such code in D, so this is a good excersise for newcomers and almost no-brainer for expirienced D user.

March 15, 2015
foo[1] is sometimes better, but not always. One has to go back to the definition of the thing and literally calculate by hand which element of the tuple you want, and then try compiling it, and so forth. Although the type system will guarantee that you eventually get it right it is a waste of time.

void external_api1_react_to_event(meters_t, time_t);
void external_api2_react_to_event(time_t, meters_t);
alias event_t = Tuple!(time_t, meters_t)
// .. some time later..
external_api1_react_to_event(event_t.get(meters_t), event_t.get(time_t));

Yes, I could say
external_api1_react_to_event(event_t[1], event_t[0])
.. but that is barbaric. It's a productivity sink because I have to go back to the original definition, align the arguments, and then context switch back to whatever I was working on before.

And yes, I could use names. But then you are subject to name clashes and using strings instead of types as member identifiers is more prone to error anyways. Ever gotten this wrong before --
void CRITICAL_TO_GET_THIS_RIGHT(uint cents, uint dollars);
....
alias params_t = Tuple!(uint, "dollars", uint, "cents");
....
params_t params;
params.dollars = 0;
params.cents = 99;
CRITICAL_TO_GET_THIS_RIGHT(params.expand);
// compilation succeeds, bank fails.

In conclusion: this is an important feature because it allows you to enforce type safety when working with tuples (e.g. in function parameters) efficiently, without taking too much of the programmer's time.

On Sunday, 15 March 2015 at 22:32:48 UTC, bearophile wrote:
> Charles Cooper:
>
>> Is there a better way to do this?
>
> Can you show some use cases for this, and isn't "foo[1]" better?
>
> Bye,
> bearophile

March 15, 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.
March 15, 2015
Sure. It is also easy to write merge sort. Or std.typetuple.Erase. Or Tuple.opIndex(size_t). But that doesn't mean everybody does it. Some utilities (and I am not saying this is, but it could be) are widely used enough that it makes sense to put them in the standard.

On Sunday, 15 March 2015 at 22:44:21 UTC, ketmar wrote:
> p.s. to be clear: it's freaking hard to do metaprogramming and template
> functional programming in c++, that's why c++ committee accepts such
> things. and it's very easy to write such code in D, so this is a good
> excersise for newcomers and almost no-brainer for expirienced D user.

March 15, 2015
Thanks for the style recommendations.

On Sunday, 15 March 2015 at 23:14:32 UTC, anonymous wrote:
> 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.
>
> 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.

« First   ‹ Prev
1 2