Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions std/typecons.d
Original file line number Diff line number Diff line change
Expand Up @@ -433,12 +433,43 @@ template Tuple(Specs...)
U u = U.init;
T t = u;
}));

/+ Helper for partial instanciation +/
template isBuildableFrom(U)
{
enum isBuildableFrom(T) = isBuildable!(T, U);
}

/+ Determine whether or not all given types in T have a common lvalue type. +/
template CommonLvalueType(T...)
{
static if (T.length == 0)
alias CommonLvalueType = void;
else static if (T.length == 1)
alias CommonLvalueType = T[0];
else static if (is(T[0] == T[1]))
alias CommonLvalueType = CommonLvalueType!(T[1 .. $]);
else static if (is(Unqual!(T[0]) == Unqual!(T[1])) && !is(T[0] == shared) && !is(T[1] == shared))
alias CommonLvalueType = CommonLvalueType!(const(Unqual!(T[0])), T[2 .. $]);
else
alias CommonLvalueType = void;
}

unittest
{
static assert(is(CommonLvalueType!int == int));
static assert(is(CommonLvalueType!(int, int) == int));
static assert(is(CommonLvalueType!(int, int, int) == int));
static assert(is(CommonLvalueType!(immutable int, immutable int, immutable int) == immutable(int)));
static assert(is(CommonLvalueType!(immutable int, int) == const(int)));
static assert(is(CommonLvalueType!(int, immutable int) == const(int)));
static assert(is(CommonLvalueType!(immutable int, const int, int) == const(int)));

static assert(is(CommonLvalueType!() == void));
static assert(is(CommonLvalueType!(int, long) == void));
static assert(is(CommonLvalueType!(int, long, float) == void));
}

struct Tuple
{
/**
Expand Down Expand Up @@ -757,6 +788,64 @@ template Tuple(Specs...)
assert(s[0] == "abc" && s[1] == 4.5);
}

/**
* Iterate fields in tuple as a _range.
*
* Only available when all field types are exactly the same in
* unqualified form. When the field types are all the same but with
* different mutability, the element type of the _range is `const`.
*
* Not available for the empty tuple.
*
* Returns:
* Slice of the fields inside this tuple; the lifetime of the
* slice must be no longer than the lifetime of the tuple.
*/
static if (Types.length && !is(CommonLvalueType!Types == void))
{
CommonLvalueType!Types[] range() @system
{
return (&expand[0])[0 .. Types.length];
}

///
nothrow unittest
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you put the unittest inside the struct, there will be a new unittest instantiated each time Tuple is instantiated. Jonathan Davis pointed this out in the newsgroup and I think it's something we should avoid.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, but it should be done for the whole type. I pointed out the same in #3594.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ndslice does this a lot, too.
AFAIK this works as long as the are enough outside tests covering all types.
See #4050

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a performance issue: code is generated and executed an unnecessary amount of times. I haven't heard of any measurements indicating that Tuple in particular is a problem but it's good to avoid in principle.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about adding version (unittest){}?

{
import std.algorithm : sort;
auto t = tuple(3, 1, 2);
t.range.sort();
assert(t == tuple(1, 2, 3));
}

///
pure unittest
{
import std.algorithm : copy, map, splitter;
import std.conv : to;

auto strCoords = "100,200,-500";
Tuple!(int, "x", int, "y", int, "z") coords;

strCoords.splitter(',')
.map!(to!int)
.copy(coords.range);

assert(coords.x == 100);
assert(coords.y == 200);
assert(coords.z == -500);
}

nothrow pure @nogc unittest
{
auto t = tuple(1, 2, 3);
auto lhs = t.range;
static immutable rhs = [1, 2, 3];
assert(lhs == rhs);

static assert(is(typeof(Tuple!(int, immutable int).init.range[0]) == const(int)));
}
}

/**
Creates a hash of this `Tuple`.

Expand Down Expand Up @@ -903,6 +992,10 @@ template Tuple(Specs...)
}
}
}

// Tuple.range depends on this being true
static if (Tuple.Types.length > 1 && !is(CommonLvalueType!(Tuple.Types) == void))
static assert(Tuple.init.expand[1].offsetof == Tuple.Types[0].sizeof);
}

///
Expand Down