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.