blob: 1ee7faaee7bc9263800d5399e1bba29e46e0d978 [file] [log] [blame]
// Written in the D programming language.
/**
This module implements a variety of type constructors, i.e., templates
that allow construction of new, useful general-purpose types.
$(SCRIPT inhibitQuickIndex = 1;)
$(DIVC quickindex,
$(BOOKTABLE,
$(TR $(TH Category) $(TH Symbols))
$(TR $(TD Tuple) $(TD
$(LREF isTuple)
$(LREF Tuple)
$(LREF tuple)
$(LREF reverse)
))
$(TR $(TD Flags) $(TD
$(LREF BitFlags)
$(LREF isBitFlagEnum)
$(LREF Flag)
$(LREF No)
$(LREF Yes)
))
$(TR $(TD Memory allocation) $(TD
$(LREF RefCounted)
$(LREF refCounted)
$(LREF RefCountedAutoInitialize)
$(LREF scoped)
$(LREF Unique)
))
$(TR $(TD Code generation) $(TD
$(LREF AutoImplement)
$(LREF BlackHole)
$(LREF generateAssertTrap)
$(LREF generateEmptyFunction)
$(LREF WhiteHole)
))
$(TR $(TD Nullable) $(TD
$(LREF Nullable)
$(LREF nullable)
$(LREF NullableRef)
$(LREF nullableRef)
))
$(TR $(TD Proxies) $(TD
$(LREF Proxy)
$(LREF rebindable)
$(LREF Rebindable)
$(LREF ReplaceType)
$(LREF unwrap)
$(LREF wrap)
))
$(TR $(TD Types) $(TD
$(LREF alignForSize)
$(LREF Ternary)
$(LREF Typedef)
$(LREF TypedefType)
$(LREF UnqualRef)
))
))
Copyright: Copyright the respective authors, 2008-
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Source: $(PHOBOSSRC std/typecons.d)
Authors: $(HTTP erdani.org, Andrei Alexandrescu),
$(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski),
Don Clugston,
Shin Fujishiro,
Kenji Hara
*/
module std.typecons;
import std.format.spec : singleSpec, FormatSpec;
import std.format.write : formatValue;
import std.meta : AliasSeq, allSatisfy;
import std.range.primitives : isOutputRange;
import std.traits;
import std.internal.attributes : betterC;
/// Value tuples
@safe unittest
{
alias Coord = Tuple!(int, "x", int, "y", int, "z");
Coord c;
c[1] = 1; // access by index
c.z = 1; // access by given name
assert(c == Coord(0, 1, 1));
// names can be omitted, types can be mixed
alias DictEntry = Tuple!(string, int);
auto dict = DictEntry("seven", 7);
// element types can be inferred
assert(tuple(2, 3, 4)[1] == 3);
// type inference works with names too
auto tup = tuple!("x", "y", "z")(2, 3, 4);
assert(tup.y == 3);
}
/// Rebindable references to const and immutable objects
@safe unittest
{
class Widget
{
void foo() const @safe {}
}
const w1 = new Widget, w2 = new Widget;
w1.foo();
// w1 = w2 would not work; can't rebind const object
auto r = Rebindable!(const Widget)(w1);
// invoke method as if r were a Widget object
r.foo();
// rebind r to refer to another object
r = w2;
}
/**
Encapsulates unique ownership of a resource.
When a `Unique!T` goes out of scope it will call `destroy`
on the resource `T` that it manages, unless it is transferred.
One important consequence of `destroy` is that it will call the
destructor of the resource `T`. GC-managed references are not
guaranteed to be valid during a destructor call, but other members of
`T`, such as file handles or pointers to `malloc` memory, will
still be valid during the destructor call. This allows the resource
`T` to deallocate or clean up any non-GC resources.
If it is desirable to persist a `Unique!T` outside of its original
scope, then it can be transferred. The transfer can be explicit, by
calling `release`, or implicit, when returning Unique from a
function. The resource `T` can be a polymorphic class object or
instance of an interface, in which case Unique behaves polymorphically
too.
If `T` is a value type, then `Unique!T` will be implemented
as a reference to a `T`.
*/
struct Unique(T)
{
/** Represents a reference to `T`. Resolves to `T*` if `T` is a value type. */
static if (is(T == class) || is(T == interface))
alias RefT = T;
else
alias RefT = T*;
public:
// Deferred in case we get some language support for checking uniqueness.
version (None)
/**
Allows safe construction of `Unique`. It creates the resource and
guarantees unique ownership of it (unless `T` publishes aliases of
`this`).
Note: Nested structs/classes cannot be created.
Params:
args = Arguments to pass to `T`'s constructor.
---
static class C {}
auto u = Unique!(C).create();
---
*/
static Unique!T create(A...)(auto ref A args)
if (__traits(compiles, new T(args)))
{
Unique!T u;
u._p = new T(args);
return u;
}
/**
Constructor that takes an rvalue.
It will ensure uniqueness, as long as the rvalue
isn't just a view on an lvalue (e.g., a cast).
Typical usage:
----
Unique!Foo f = new Foo;
----
*/
this(RefT p)
{
_p = p;
}
/**
Constructor that takes an lvalue. It nulls its source.
The nulling will ensure uniqueness as long as there
are no previous aliases to the source.
*/
this(ref RefT p)
{
_p = p;
p = null;
assert(p is null);
}
/**
Constructor that takes a `Unique` of a type that is convertible to our type.
Typically used to transfer a `Unique` rvalue of derived type to
a `Unique` of base type.
Example:
---
class C : Object {}
Unique!C uc = new C;
Unique!Object uo = uc.release;
---
*/
this(U)(Unique!U u)
if (is(u.RefT:RefT))
{
_p = u._p;
u._p = null;
}
/// Transfer ownership from a `Unique` of a type that is convertible to our type.
void opAssign(U)(Unique!U u)
if (is(u.RefT:RefT))
{
// first delete any resource we own
destroy(this);
_p = u._p;
u._p = null;
}
~this()
{
if (_p !is null)
{
destroy(_p);
_p = null;
}
}
/** Returns whether the resource exists. */
@property bool isEmpty() const
{
return _p is null;
}
/** Transfer ownership to a `Unique` rvalue. Nullifies the current contents.
Same as calling std.algorithm.move on it.
*/
Unique release()
{
import std.algorithm.mutation : move;
return this.move;
}
/** Forwards member access to contents. */
mixin Proxy!_p;
/**
Postblit operator is undefined to prevent the cloning of `Unique` objects.
*/
@disable this(this);
private:
RefT _p;
}
///
@safe unittest
{
static struct S
{
int i;
this(int i){this.i = i;}
}
Unique!S produce()
{
// Construct a unique instance of S on the heap
Unique!S ut = new S(5);
// Implicit transfer of ownership
return ut;
}
// Borrow a unique resource by ref
void increment(ref Unique!S ur)
{
ur.i++;
}
void consume(Unique!S u2)
{
assert(u2.i == 6);
// Resource automatically deleted here
}
Unique!S u1;
assert(u1.isEmpty);
u1 = produce();
increment(u1);
assert(u1.i == 6);
//consume(u1); // Error: u1 is not copyable
// Transfer ownership of the resource
consume(u1.release);
assert(u1.isEmpty);
}
@system unittest
{
// test conversion to base ref
int deleted = 0;
class C
{
~this(){deleted++;}
}
// constructor conversion
Unique!Object u = Unique!C(new C);
static assert(!__traits(compiles, {u = new C;}));
assert(!u.isEmpty);
destroy(u);
assert(deleted == 1);
Unique!C uc = new C;
static assert(!__traits(compiles, {Unique!Object uo = uc;}));
Unique!Object uo = new C;
// opAssign conversion, deleting uo resource first
uo = uc.release;
assert(uc.isEmpty);
assert(!uo.isEmpty);
assert(deleted == 2);
}
@system unittest
{
class Bar
{
~this() { debug(Unique) writeln(" Bar destructor"); }
int val() const { return 4; }
}
alias UBar = Unique!(Bar);
UBar g(UBar u)
{
debug(Unique) writeln("inside g");
return u.release;
}
auto ub = UBar(new Bar);
assert(!ub.isEmpty);
assert(ub.val == 4);
static assert(!__traits(compiles, {auto ub3 = g(ub);}));
auto ub2 = g(ub.release);
assert(ub.isEmpty);
assert(!ub2.isEmpty);
}
@system unittest
{
interface Bar
{
int val() const;
}
class BarImpl : Bar
{
static int count;
this()
{
count++;
}
~this()
{
count--;
}
int val() const { return 4; }
}
alias UBar = Unique!Bar;
UBar g(UBar u)
{
debug(Unique) writeln("inside g");
return u.release;
}
void consume(UBar u)
{
assert(u.val() == 4);
// Resource automatically deleted here
}
auto ub = UBar(new BarImpl);
assert(BarImpl.count == 1);
assert(!ub.isEmpty);
assert(ub.val == 4);
static assert(!__traits(compiles, {auto ub3 = g(ub);}));
auto ub2 = g(ub.release);
assert(ub.isEmpty);
assert(!ub2.isEmpty);
consume(ub2.release);
assert(BarImpl.count == 0);
}
@safe unittest
{
struct Foo
{
~this() { }
int val() const { return 3; }
@disable this(this);
}
alias UFoo = Unique!(Foo);
UFoo f(UFoo u)
{
return u.release;
}
auto uf = UFoo(new Foo);
assert(!uf.isEmpty);
assert(uf.val == 3);
static assert(!__traits(compiles, {auto uf3 = f(uf);}));
auto uf2 = f(uf.release);
assert(uf.isEmpty);
assert(!uf2.isEmpty);
}
// ensure Unique behaves correctly through const access paths
@system unittest
{
struct Bar {int val;}
struct Foo
{
Unique!Bar bar = new Bar;
}
Foo foo;
foo.bar.val = 6;
const Foo* ptr = &foo;
static assert(is(typeof(ptr) == const(Foo*)));
static assert(is(typeof(ptr.bar) == const(Unique!Bar)));
static assert(is(typeof(ptr.bar.val) == const(int)));
assert(ptr.bar.val == 6);
foo.bar.val = 7;
assert(ptr.bar.val == 7);
}
// Used in Tuple.toString
private template sharedToString(alias field)
if (is(typeof(field) == shared))
{
static immutable sharedToString = typeof(field).stringof;
}
private template sharedToString(alias field)
if (!is(typeof(field) == shared))
{
alias sharedToString = field;
}
private enum bool distinctFieldNames(names...) = __traits(compiles,
{
static foreach (__name; names)
static if (is(typeof(__name) : string))
mixin("enum int " ~ __name ~ " = 0;");
});
@safe unittest
{
static assert(!distinctFieldNames!(string, "abc", string, "abc"));
static assert(distinctFieldNames!(string, "abc", int, "abd"));
static assert(!distinctFieldNames!(int, "abc", string, "abd", int, "abc"));
// https://issues.dlang.org/show_bug.cgi?id=19240
static assert(!distinctFieldNames!(int, "int"));
}
// Parse (type,name) pairs (FieldSpecs) out of the specified
// arguments. Some fields would have name, others not.
private template parseSpecs(Specs...)
{
static if (Specs.length == 0)
{
alias parseSpecs = AliasSeq!();
}
else static if (is(Specs[0]))
{
static if (is(typeof(Specs[1]) : string))
{
alias parseSpecs =
AliasSeq!(FieldSpec!(Specs[0 .. 2]),
parseSpecs!(Specs[2 .. $]));
}
else
{
alias parseSpecs =
AliasSeq!(FieldSpec!(Specs[0]),
parseSpecs!(Specs[1 .. $]));
}
}
else
{
static assert(0, "Attempted to instantiate Tuple with an "
~"invalid argument: "~ Specs[0].stringof);
}
}
private template FieldSpec(T, string s = "")
{
alias Type = T;
alias name = s;
}
// Used with staticMap.
private alias extractType(alias spec) = spec.Type;
private alias extractName(alias spec) = spec.name;
private template expandSpec(alias spec)
{
static if (spec.name.length == 0)
alias expandSpec = AliasSeq!(spec.Type);
else
alias expandSpec = AliasSeq!(spec.Type, spec.name);
}
private enum areCompatibleTuples(Tup1, Tup2, string op) =
isTuple!(OriginalType!Tup2) && Tup1.Types.length == Tup2.Types.length && is(typeof(
(ref Tup1 tup1, ref Tup2 tup2)
{
static foreach (i; 0 .. Tup1.Types.length)
{{
auto lhs = typeof(tup1.field[i]).init;
auto rhs = typeof(tup2.field[i]).init;
static if (op == "=")
lhs = rhs;
else
auto result = mixin("lhs "~op~" rhs");
}}
}));
private enum areBuildCompatibleTuples(Tup1, Tup2) =
isTuple!Tup2 && Tup1.Types.length == Tup2.Types.length && is(typeof(
{
static foreach (i; 0 .. Tup1.Types.length)
static assert(isBuildable!(Tup1.Types[i], Tup2.Types[i]));
}));
// Returns `true` iff a `T` can be initialized from a `U`.
private enum isBuildable(T, U) = is(typeof(
{
U u = U.init;
T t = u;
}));
// Helper for partial instantiation
private template isBuildableFrom(U)
{
enum isBuildableFrom(T) = isBuildable!(T, U);
}
/**
_Tuple of values, for example $(D Tuple!(int, string)) is a record that
stores an `int` and a `string`. `Tuple` can be used to bundle
values together, notably when returning multiple values from a
function. If `obj` is a `Tuple`, the individual members are
accessible with the syntax `obj[0]` for the first field, `obj[1]`
for the second, and so on.
See_Also: $(LREF tuple).
Params:
Specs = A list of types (and optionally, member names) that the `Tuple` contains.
*/
template Tuple(Specs...)
if (distinctFieldNames!(Specs))
{
import std.meta : staticMap;
alias fieldSpecs = parseSpecs!Specs;
// Generates named fields as follows:
// alias name_0 = Identity!(field[0]);
// alias name_1 = Identity!(field[1]);
// :
// NOTE: field[k] is an expression (which yields a symbol of a
// variable) and can't be aliased directly.
enum injectNamedFields = ()
{
string decl = "";
static foreach (i, val; fieldSpecs)
{{
immutable si = i.stringof;
decl ~= "alias _" ~ si ~ " = Identity!(field[" ~ si ~ "]);";
if (val.name.length != 0)
{
decl ~= "alias " ~ val.name ~ " = _" ~ si ~ ";";
}
}}
return decl;
};
// Returns Specs for a subtuple this[from .. to] preserving field
// names if any.
alias sliceSpecs(size_t from, size_t to) =
staticMap!(expandSpec, fieldSpecs[from .. to]);
struct Tuple
{
/**
* The types of the `Tuple`'s components.
*/
alias Types = staticMap!(extractType, fieldSpecs);
private alias _Fields = Specs;
///
static if (Specs.length == 0) @safe unittest
{
import std.meta : AliasSeq;
alias Fields = Tuple!(int, "id", string, float);
static assert(is(Fields.Types == AliasSeq!(int, string, float)));
}
/**
* The names of the `Tuple`'s components. Unnamed fields have empty names.
*/
alias fieldNames = staticMap!(extractName, fieldSpecs);
///
static if (Specs.length == 0) @safe unittest
{
import std.meta : AliasSeq;
alias Fields = Tuple!(int, "id", string, float);
static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
}
/**
* Use `t.expand` for a `Tuple` `t` to expand it into its
* components. The result of `expand` acts as if the `Tuple`'s components
* were listed as a list of values. (Ordinarily, a `Tuple` acts as a
* single value.)
*/
Types expand;
mixin(injectNamedFields());
///
static if (Specs.length == 0) @safe unittest
{
auto t1 = tuple(1, " hello ", 'a');
assert(t1.toString() == `Tuple!(int, string, char)(1, " hello ", 'a')`);
void takeSeveralTypes(int n, string s, bool b)
{
assert(n == 4 && s == "test" && b == false);
}
auto t2 = tuple(4, "test", false);
//t.expand acting as a list of values
takeSeveralTypes(t2.expand);
}
static if (is(Specs))
{
// This is mostly to make t[n] work.
alias expand this;
}
else
{
@property
ref inout(Tuple!Types) _Tuple_super() inout @trusted
{
static foreach (i; 0 .. Types.length) // Rely on the field layout
{
static assert(typeof(return).init.tupleof[i].offsetof ==
expand[i].offsetof);
}
return *cast(typeof(return)*) &(field[0]);
}
// This is mostly to make t[n] work.
alias _Tuple_super this;
}
// backwards compatibility
alias field = expand;
/**
* Constructor taking one value for each field.
*
* Params:
* values = A list of values that are either the same
* types as those given by the `Types` field
* of this `Tuple`, or can implicitly convert
* to those types. They must be in the same
* order as they appear in `Types`.
*/
static if (Types.length > 0)
{
this(Types values)
{
field[] = values[];
}
}
///
static if (Specs.length == 0) @safe unittest
{
alias ISD = Tuple!(int, string, double);
auto tup = ISD(1, "test", 3.2);
assert(tup.toString() == `Tuple!(int, string, double)(1, "test", 3.2)`);
}
/**
* Constructor taking a compatible array.
*
* Params:
* values = A compatible static array to build the `Tuple` from.
* Array slices are not supported.
*/
this(U, size_t n)(U[n] values)
if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types))
{
static foreach (i; 0 .. Types.length)
{
field[i] = values[i];
}
}
///
static if (Specs.length == 0) @safe unittest
{
int[2] ints;
Tuple!(int, int) t = ints;
}
/**
* Constructor taking a compatible `Tuple`. Two `Tuple`s are compatible
* $(B iff) they are both of the same length, and, for each type `T` on the
* left-hand side, the corresponding type `U` on the right-hand side can
* implicitly convert to `T`.
*
* Params:
* another = A compatible `Tuple` to build from. Its type must be
* compatible with the target `Tuple`'s type.
*/
this(U)(U another)
if (areBuildCompatibleTuples!(typeof(this), U))
{
field[] = another.field[];
}
///
static if (Specs.length == 0) @safe unittest
{
alias IntVec = Tuple!(int, int, int);
alias DubVec = Tuple!(double, double, double);
IntVec iv = tuple(1, 1, 1);
//Ok, int can implicitly convert to double
DubVec dv = iv;
//Error: double cannot implicitly convert to int
//IntVec iv2 = dv;
}
/**
* Comparison for equality. Two `Tuple`s are considered equal
* $(B iff) they fulfill the following criteria:
*
* $(UL
* $(LI Each `Tuple` is the same length.)
* $(LI For each type `T` on the left-hand side and each type
* `U` on the right-hand side, values of type `T` can be
* compared with values of type `U`.)
* $(LI For each value `v1` on the left-hand side and each value
* `v2` on the right-hand side, the expression `v1 == v2` is
* true.))
*
* Params:
* rhs = The `Tuple` to compare against. It must meeting the criteria
* for comparison between `Tuple`s.
*
* Returns:
* true if both `Tuple`s are equal, otherwise false.
*/
bool opEquals(R)(R rhs)
if (areCompatibleTuples!(typeof(this), R, "=="))
{
return field[] == rhs.field[];
}
/// ditto
bool opEquals(R)(R rhs) const
if (areCompatibleTuples!(typeof(this), R, "=="))
{
return field[] == rhs.field[];
}
/// ditto
bool opEquals(R...)(auto ref R rhs)
if (R.length > 1 && areCompatibleTuples!(typeof(this), Tuple!R, "=="))
{
static foreach (i; 0 .. Types.length)
if (field[i] != rhs[i])
return false;
return true;
}
///
static if (Specs.length == 0) @safe unittest
{
Tuple!(int, string) t1 = tuple(1, "test");
Tuple!(double, string) t2 = tuple(1.0, "test");
//Ok, int can be compared with double and
//both have a value of 1
assert(t1 == t2);
}
/**
* Comparison for ordering.
*
* Params:
* rhs = The `Tuple` to compare against. It must meet the criteria
* for comparison between `Tuple`s.
*
* Returns:
* For any values `v1` contained by the left-hand side tuple and any
* values `v2` contained by the right-hand side:
*
* 0 if `v1 == v2` for all members or the following value for the
* first position were the mentioned criteria is not satisfied:
*
* $(UL
* $(LI NaN, in case one of the operands is a NaN.)
* $(LI A negative number if the expression `v1 < v2` is true.)
* $(LI A positive number if the expression `v1 > v2` is true.))
*/
auto opCmp(R)(R rhs)
if (areCompatibleTuples!(typeof(this), R, "<"))
{
static foreach (i; 0 .. Types.length)
{
if (field[i] != rhs.field[i])
{
import std.math.traits : isNaN;
static if (isFloatingPoint!(Types[i]))
{
if (isNaN(field[i]))
return float.nan;
}
static if (isFloatingPoint!(typeof(rhs.field[i])))
{
if (isNaN(rhs.field[i]))
return float.nan;
}
static if (is(typeof(field[i].opCmp(rhs.field[i]))) &&
isFloatingPoint!(typeof(field[i].opCmp(rhs.field[i]))))
{
if (isNaN(field[i].opCmp(rhs.field[i])))
return float.nan;
}
return field[i] < rhs.field[i] ? -1 : 1;
}
}
return 0;
}
/// ditto
auto opCmp(R)(R rhs) const
if (areCompatibleTuples!(typeof(this), R, "<"))
{
static foreach (i; 0 .. Types.length)
{
if (field[i] != rhs.field[i])
{
import std.math.traits : isNaN;
static if (isFloatingPoint!(Types[i]))
{
if (isNaN(field[i]))
return float.nan;
}
static if (isFloatingPoint!(typeof(rhs.field[i])))
{
if (isNaN(rhs.field[i]))
return float.nan;
}
static if (is(typeof(field[i].opCmp(rhs.field[i]))) &&
isFloatingPoint!(typeof(field[i].opCmp(rhs.field[i]))))
{
if (isNaN(field[i].opCmp(rhs.field[i])))
return float.nan;
}
return field[i] < rhs.field[i] ? -1 : 1;
}
}
return 0;
}
/**
The first `v1` for which `v1 > v2` is true determines
the result. This could lead to unexpected behaviour.
*/
static if (Specs.length == 0) @safe unittest
{
auto tup1 = tuple(1, 1, 1);
auto tup2 = tuple(1, 100, 100);
assert(tup1 < tup2);
//Only the first result matters for comparison
tup1[0] = 2;
assert(tup1 > tup2);
}
/**
Concatenate Tuples.
Tuple concatenation is only allowed if all named fields are distinct (no named field of this tuple occurs in `t`
and no named field of `t` occurs in this tuple).
Params:
t = The `Tuple` to concatenate with
Returns: A concatenation of this tuple and `t`
*/
auto opBinary(string op, T)(auto ref T t)
if (op == "~" && !(is(T : U[], U) && isTuple!U))
{
static if (isTuple!T)
{
static assert(distinctFieldNames!(_Fields, T._Fields),
"Cannot concatenate tuples with duplicate fields: " ~ fieldNames.stringof ~
" - " ~ T.fieldNames.stringof);
return Tuple!(_Fields, T._Fields)(expand, t.expand);
}
else
{
return Tuple!(_Fields, T)(expand, t);
}
}
/// ditto
auto opBinaryRight(string op, T)(auto ref T t)
if (op == "~" && !(is(T : U[], U) && isTuple!U))
{
static if (isTuple!T)
{
static assert(distinctFieldNames!(_Fields, T._Fields),
"Cannot concatenate tuples with duplicate fields: " ~ T.stringof ~
" - " ~ fieldNames.fieldNames.stringof);
return Tuple!(T._Fields, _Fields)(t.expand, expand);
}
else
{
return Tuple!(T, _Fields)(t, expand);
}
}
/**
* Assignment from another `Tuple`.
*
* Params:
* rhs = The source `Tuple` to assign from. Each element of the
* source `Tuple` must be implicitly assignable to each
* respective element of the target `Tuple`.
*/
ref Tuple opAssign(R)(auto ref R rhs)
if (areCompatibleTuples!(typeof(this), R, "="))
{
import std.algorithm.mutation : swap;
static if (is(R : Tuple!Types) && !__traits(isRef, rhs) && isTuple!R)
{
if (__ctfe)
{
// Cannot use swap at compile time
field[] = rhs.field[];
}
else
{
// Use swap-and-destroy to optimize rvalue assignment
swap!(Tuple!Types)(this, rhs);
}
}
else
{
// Do not swap; opAssign should be called on the fields.
field[] = rhs.field[];
}
return this;
}
/**
* Renames the elements of a $(LREF Tuple).
*
* `rename` uses the passed `names` and returns a new
* $(LREF Tuple) using these names, with the content
* unchanged.
* If fewer names are passed than there are members
* of the $(LREF Tuple) then those trailing members are unchanged.
* An empty string will remove the name for that member.
* It is an compile-time error to pass more names than
* there are members of the $(LREF Tuple).
*/
ref rename(names...)() inout return
if (names.length == 0 || allSatisfy!(isSomeString, typeof(names)))
{
import std.algorithm.comparison : equal;
// to circumvent https://issues.dlang.org/show_bug.cgi?id=16418
static if (names.length == 0 || equal([names], [fieldNames]))
return this;
else
{
enum nT = Types.length;
enum nN = names.length;
static assert(nN <= nT, "Cannot have more names than tuple members");
alias allNames = AliasSeq!(names, fieldNames[nN .. $]);
import std.meta : Alias, aliasSeqOf;
template GetItem(size_t idx)
{
import std.array : empty;
static if (idx < nT)
alias GetItem = Alias!(Types[idx]);
else static if (allNames[idx - nT].empty)
alias GetItem = AliasSeq!();
else
alias GetItem = Alias!(allNames[idx - nT]);
}
import std.range : roundRobin, iota;
alias NewTupleT = Tuple!(staticMap!(GetItem, aliasSeqOf!(
roundRobin(iota(nT), iota(nT, 2*nT)))));
return *(() @trusted => cast(NewTupleT*)&this)();
}
}
///
static if (Specs.length == 0) @safe unittest
{
auto t0 = tuple(4, "hello");
auto t0Named = t0.rename!("val", "tag");
assert(t0Named.val == 4);
assert(t0Named.tag == "hello");
Tuple!(float, "dat", size_t[2], "pos") t1;
t1.pos = [2, 1];
auto t1Named = t1.rename!"height";
t1Named.height = 3.4f;
assert(t1Named.height == 3.4f);
assert(t1Named.pos == [2, 1]);
t1Named.rename!"altitude".altitude = 5;
assert(t1Named.height == 5);
Tuple!(int, "a", int, int, "c") t2;
t2 = tuple(3,4,5);
auto t2Named = t2.rename!("", "b");
// "a" no longer has a name
static assert(!__traits(hasMember, typeof(t2Named), "a"));
assert(t2Named[0] == 3);
assert(t2Named.b == 4);
assert(t2Named.c == 5);
// not allowed to specify more names than the tuple has members
static assert(!__traits(compiles, t2.rename!("a","b","c","d")));
// use it in a range pipeline
import std.range : iota, zip;
import std.algorithm.iteration : map, sum;
auto res = zip(iota(1, 4), iota(10, 13))
.map!(t => t.rename!("a", "b"))
.map!(t => t.a * t.b)
.sum;
assert(res == 68);
const tup = Tuple!(int, "a", int, "b")(2, 3);
const renamed = tup.rename!("c", "d");
assert(renamed.c + renamed.d == 5);
}
/**
* Overload of $(LREF _rename) that takes an associative array
* `translate` as a template parameter, where the keys are
* either the names or indices of the members to be changed
* and the new names are the corresponding values.
* Every key in `translate` must be the name of a member of the
* $(LREF tuple).
* The same rules for empty strings apply as for the variadic
* template overload of $(LREF _rename).
*/
ref rename(alias translate)() inout
if (is(typeof(translate) : V[K], V, K) && isSomeString!V &&
(isSomeString!K || is(K : size_t)))
{
import std.meta : aliasSeqOf;
import std.range : ElementType;
static if (isSomeString!(ElementType!(typeof(translate.keys))))
{
{
import std.conv : to;
import std.algorithm.iteration : filter;
import std.algorithm.searching : canFind;
enum notFound = translate.keys
.filter!(k => fieldNames.canFind(k) == -1);
static assert(notFound.empty, "Cannot find members "
~ notFound.to!string ~ " in type "
~ typeof(this).stringof);
}
return this.rename!(aliasSeqOf!(
{
import std.array : empty;
auto names = [fieldNames];
foreach (ref n; names)
if (!n.empty)
if (auto p = n in translate)
n = *p;
return names;
}()));
}
else
{
{
import std.algorithm.iteration : filter;
import std.conv : to;
enum invalid = translate.keys.
filter!(k => k < 0 || k >= this.length);
static assert(invalid.empty, "Indices " ~ invalid.to!string
~ " are out of bounds for tuple with length "
~ this.length.to!string);
}
return this.rename!(aliasSeqOf!(
{
auto names = [fieldNames];
foreach (k, v; translate)
names[k] = v;
return names;
}()));
}
}
///
static if (Specs.length == 0) @safe unittest
{
//replacing names by their current name
Tuple!(float, "dat", size_t[2], "pos") t1;
t1.pos = [2, 1];
auto t1Named = t1.rename!(["dat": "height"]);
t1Named.height = 3.4;
assert(t1Named.pos == [2, 1]);
t1Named.rename!(["height": "altitude"]).altitude = 5;
assert(t1Named.height == 5);
Tuple!(int, "a", int, "b") t2;
t2 = tuple(3, 4);
auto t2Named = t2.rename!(["a": "b", "b": "c"]);
assert(t2Named.b == 3);
assert(t2Named.c == 4);
const t3 = Tuple!(int, "a", int, "b")(3, 4);
const t3Named = t3.rename!(["a": "b", "b": "c"]);
assert(t3Named.b == 3);
assert(t3Named.c == 4);
}
///
static if (Specs.length == 0) @safe unittest
{
//replace names by their position
Tuple!(float, "dat", size_t[2], "pos") t1;
t1.pos = [2, 1];
auto t1Named = t1.rename!([0: "height"]);
t1Named.height = 3.4;
assert(t1Named.pos == [2, 1]);
t1Named.rename!([0: "altitude"]).altitude = 5;
assert(t1Named.height == 5);
Tuple!(int, "a", int, "b", int, "c") t2;
t2 = tuple(3, 4, 5);
auto t2Named = t2.rename!([0: "c", 2: "a"]);
assert(t2Named.a == 5);
assert(t2Named.b == 4);
assert(t2Named.c == 3);
}
static if (Specs.length == 0) @safe unittest
{
//check that empty translations work fine
enum string[string] a0 = null;
enum string[int] a1 = null;
Tuple!(float, "a", float, "b") t0;
auto t1 = t0.rename!a0;
t1.a = 3;
t1.b = 4;
auto t2 = t0.rename!a1;
t2.a = 3;
t2.b = 4;
auto t3 = t0.rename;
t3.a = 3;
t3.b = 4;
}
/**
* Takes a slice by-reference of this `Tuple`.
*
* Params:
* from = A `size_t` designating the starting position of the slice.
* to = A `size_t` designating the ending position (exclusive) of the slice.
*
* Returns:
* A new `Tuple` that is a slice from `[from, to$(RPAREN)` of the original.
* It has the same types and values as the range `[from, to$(RPAREN)` in
* the original.
*/
@property
ref inout(Tuple!(sliceSpecs!(from, to))) slice(size_t from, size_t to)() inout @trusted
if (from <= to && to <= Types.length)
{
static assert(
(typeof(this).alignof % typeof(return).alignof == 0) &&
(expand[from].offsetof % typeof(return).alignof == 0),
"Slicing by reference is impossible because of an alignment mistmatch" ~
" (See https://issues.dlang.org/show_bug.cgi?id=15645).");
return *cast(typeof(return)*) &(field[from]);
}
///
static if (Specs.length == 0) @safe unittest
{
Tuple!(int, string, float, double) a;
a[1] = "abc";
a[2] = 4.5;
auto s = a.slice!(1, 3);
static assert(is(typeof(s) == Tuple!(string, float)));
assert(s[0] == "abc" && s[1] == 4.5);
// https://issues.dlang.org/show_bug.cgi?id=15645
Tuple!(int, short, bool, double) b;
static assert(!__traits(compiles, b.slice!(2, 4)));
}
/**
Creates a hash of this `Tuple`.
Returns:
A `size_t` representing the hash of this `Tuple`.
*/
size_t toHash() const nothrow @safe
{
size_t h = 0;
static foreach (i, T; Types)
{{
static if (__traits(compiles, h = .hashOf(field[i])))
const k = .hashOf(field[i]);
else
{
// Workaround for when .hashOf is not both @safe and nothrow.
static if (is(T : shared U, U) && __traits(compiles, (U* a) nothrow @safe => .hashOf(*a))
&& !__traits(hasMember, T, "toHash"))
// BUG: Improperly casts away `shared`!
const k = .hashOf(*(() @trusted => cast(U*) &field[i])());
else
// BUG: Improperly casts away `shared`!
const k = typeid(T).getHash((() @trusted => cast(const void*) &field[i])());
}
static if (i == 0)
h = k;
else
// As in boost::hash_combine
// https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine
h ^= k + 0x9e3779b9 + (h << 6) + (h >>> 2);
}}
return h;
}
/**
* Converts to string.
*
* Returns:
* The string representation of this `Tuple`.
*/
string toString()() const
{
import std.array : appender;
auto app = appender!string();
this.toString((const(char)[] chunk) => app ~= chunk);
return app.data;
}
import std.format.spec : FormatSpec;
/**
* Formats `Tuple` with either `%s`, `%(inner%)` or `%(inner%|sep%)`.
*
* $(TABLE2 Formats supported by Tuple,
* $(THEAD Format, Description)
* $(TROW $(P `%s`), $(P Format like `Tuple!(types)(elements formatted with %s each)`.))
* $(TROW $(P `%(inner%)`), $(P The format `inner` is applied the expanded `Tuple`$(COMMA) so
* it may contain as many formats as the `Tuple` has fields.))
* $(TROW $(P `%(inner%|sep%)`), $(P The format `inner` is one format$(COMMA) that is applied
* on all fields of the `Tuple`. The inner format must be compatible to all
* of them.)))
*
* Params:
* sink = A `char` accepting delegate
* fmt = A $(REF FormatSpec, std,format)
*/
void toString(DG)(scope DG sink) const
{
auto f = FormatSpec!char();
toString(sink, f);
}
/// ditto
void toString(DG, Char)(scope DG sink, scope const ref FormatSpec!Char fmt) const
{
import std.format : format, FormatException;
import std.format.write : formattedWrite;
import std.range : only;
if (fmt.nested)
{
if (fmt.sep)
{
foreach (i, Type; Types)
{
static if (i > 0)
{
sink(fmt.sep);
}
// TODO: Change this once formattedWrite() works for shared objects.
static if (is(Type == class) && is(Type == shared))
{
sink(Type.stringof);
}
else
{
formattedWrite(sink, fmt.nested, this.field[i]);
}
}
}
else
{
formattedWrite(sink, fmt.nested, staticMap!(sharedToString, this.expand));
}
}
else if (fmt.spec == 's')
{
enum header = Unqual!(typeof(this)).stringof ~ "(",
footer = ")",
separator = ", ";
sink(header);
foreach (i, Type; Types)
{
static if (i > 0)
{
sink(separator);
}
// TODO: Change this once format() works for shared objects.
static if (is(Type == class) && is(Type == shared))
{
sink(Type.stringof);
}
else
{
sink(format!("%(%s%)")(only(field[i])));
}
}
sink(footer);
}
else
{
const spec = fmt.spec;
throw new FormatException(
"Expected '%s' or '%(...%)' or '%(...%|...%)' format specifier for type '" ~
Unqual!(typeof(this)).stringof ~ "', not '%" ~ spec ~ "'.");
}
}
///
static if (Specs.length == 0) @safe unittest
{
import std.format : format;
Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];
// Default format
assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`);
// One Format for each individual component
assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`);
assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`);
// One Format for all components
assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`);
// Array of Tuples
assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`);
}
///
static if (Specs.length == 0) @safe unittest
{
import std.exception : assertThrown;
import std.format : format, FormatException;
// Error: %( %) missing.
assertThrown!FormatException(
format("%d, %f", tuple(1, 2.0)) == `1, 2.0`
);
// Error: %( %| %) missing.
assertThrown!FormatException(
format("%d", tuple(1, 2)) == `1, 2`
);
// Error: %d inadequate for double
assertThrown!FormatException(
format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0`
);
}
}
}
///
@safe unittest
{
Tuple!(int, int) point;
// assign coordinates
point[0] = 5;
point[1] = 6;
// read coordinates
auto x = point[0];
auto y = point[1];
}
/**
`Tuple` members can be named. It is legal to mix named and unnamed
members. The method above is still applicable to all fields.
*/
@safe unittest
{
alias Entry = Tuple!(int, "index", string, "value");
Entry e;
e.index = 4;
e.value = "Hello";
assert(e[1] == "Hello");
assert(e[0] == 4);
}
/**
A `Tuple` with named fields is a distinct type from a `Tuple` with unnamed
fields, i.e. each naming imparts a separate type for the `Tuple`. Two
`Tuple`s differing in naming only are still distinct, even though they
might have the same structure.
*/
@safe unittest
{
Tuple!(int, "x", int, "y") point1;
Tuple!(int, int) point2;
assert(!is(typeof(point1) == typeof(point2)));
}
/// Use tuples as ranges
@safe unittest
{
import std.algorithm.iteration : sum;
import std.range : only;
auto t = tuple(1, 2);
assert(t.expand.only.sum == 3);
}
// https://issues.dlang.org/show_bug.cgi?id=4582
@safe unittest
{
static assert(!__traits(compiles, Tuple!(string, "id", int, "id")));
static assert(!__traits(compiles, Tuple!(string, "str", int, "i", string, "str", float)));
}
/// Concatenate tuples
@safe unittest
{
import std.meta : AliasSeq;
auto t = tuple(1, "2") ~ tuple(ushort(42), true);
static assert(is(t.Types == AliasSeq!(int, string, ushort, bool)));
assert(t[1] == "2");
assert(t[2] == 42);
assert(t[3] == true);
}
// https://issues.dlang.org/show_bug.cgi?id=14637
// tuple concat
@safe unittest
{
auto t = tuple!"foo"(1.0) ~ tuple!"bar"("3");
static assert(is(t.Types == AliasSeq!(double, string)));
static assert(t.fieldNames == tuple("foo", "bar"));
assert(t.foo == 1.0);
assert(t.bar == "3");
}
// https://issues.dlang.org/show_bug.cgi?id=18824
// tuple concat
@safe unittest
{
alias Type = Tuple!(int, string);
Type[] arr;
auto t = tuple(2, "s");
// Test opBinaryRight
arr = arr ~ t;
// Test opBinary
arr = t ~ arr;
static assert(is(typeof(arr) == Type[]));
immutable Type[] b;
auto c = b ~ t;
static assert(is(typeof(c) == immutable(Type)[]));
}
// tuple concat
@safe unittest
{
auto t = tuple!"foo"(1.0) ~ "3";
static assert(is(t.Types == AliasSeq!(double, string)));
assert(t.foo == 1.0);
assert(t[1]== "3");
}
// tuple concat
@safe unittest
{
auto t = "2" ~ tuple!"foo"(1.0);
static assert(is(t.Types == AliasSeq!(string, double)));
assert(t.foo == 1.0);
assert(t[0]== "2");
}
// tuple concat
@safe unittest
{
auto t = "2" ~ tuple!"foo"(1.0) ~ tuple(42, 3.0f) ~ real(1) ~ "a";
static assert(is(t.Types == AliasSeq!(string, double, int, float, real, string)));
assert(t.foo == 1.0);
assert(t[0] == "2");
assert(t[1] == 1.0);
assert(t[2] == 42);
assert(t[3] == 3.0f);
assert(t[4] == 1.0);
assert(t[5] == "a");
}
// ensure that concatenation of tuples with non-distinct fields is forbidden
@safe unittest
{
static assert(!__traits(compiles,
tuple!("a")(0) ~ tuple!("a")("1")));
static assert(!__traits(compiles,
tuple!("a", "b")(0, 1) ~ tuple!("b", "a")("3", 1)));
static assert(!__traits(compiles,
tuple!("a")(0) ~ tuple!("b", "a")("3", 1)));
static assert(!__traits(compiles,
tuple!("a1", "a")(1.0, 0) ~ tuple!("a2", "a")("3", 0)));
}
// Ensure that Tuple comparison with non-const opEquals works
@safe unittest
{
static struct Bad
{
int a;
bool opEquals(Bad b)
{
return a == b.a;
}
}
auto t = Tuple!(int, Bad, string)(1, Bad(1), "asdf");
//Error: mutable method Bad.opEquals is not callable using a const object
assert(t == AliasSeq!(1, Bad(1), "asdf"));
}
// Ensure Tuple.toHash works
@safe unittest
{
Tuple!(int, int) point;
assert(point.toHash == typeof(point).init.toHash);
assert(tuple(1, 2) != point);
assert(tuple(1, 2) == tuple(1, 2));
point[0] = 1;
assert(tuple(1, 2) != point);
point[1] = 2;
assert(tuple(1, 2) == point);
}
@safe @betterC unittest
{
auto t = tuple(1, 2);
assert(t == tuple(1, 2));
auto t3 = tuple(1, 'd');
}
// https://issues.dlang.org/show_bug.cgi?id=20850
// Assignment to enum tuple
@safe unittest
{
enum T : Tuple!(int*) { a = T(null) }
T t;
t = T.a;
}
// https://issues.dlang.org/show_bug.cgi?id=13663
@safe unittest
{
auto t = tuple(real.nan);
assert(!(t > t));
assert(!(t < t));
assert(!(t == t));
}
@safe unittest
{
struct S
{
float opCmp(S s) { return float.nan; }
bool opEquals(S s) { return false; }
}
auto t = tuple(S());
assert(!(t > t));
assert(!(t < t));
assert(!(t == t));
}
// https://issues.dlang.org/show_bug.cgi?id=8015
@safe unittest
{
struct MyStruct
{
string str;
@property string toStr()
{
return str;
}
alias toStr this;
}
Tuple!(MyStruct) t;
}
/**
Creates a copy of a $(LREF Tuple) with its fields in _reverse order.
Params:
t = The `Tuple` to copy.
Returns:
A new `Tuple`.
*/
auto reverse(T)(T t)
if (isTuple!T)
{
import std.meta : Reverse;
// @@@BUG@@@ Cannot be an internal function due to forward reference issues.
// @@@BUG@@@ 9929 Need 'this' when calling template with expanded tuple
// return tuple(Reverse!(t.expand));
ReverseTupleType!T result;
auto tup = t.expand;
result.expand = Reverse!tup;
return result;
}
///
@safe unittest
{
auto tup = tuple(1, "2");
assert(tup.reverse == tuple("2", 1));
}
/* Get a Tuple type with the reverse specification of Tuple T. */
private template ReverseTupleType(T)
if (isTuple!T)
{
static if (is(T : Tuple!A, A...))
alias ReverseTupleType = Tuple!(ReverseTupleSpecs!A);
}
/* Reverse the Specs of a Tuple. */
private template ReverseTupleSpecs(T...)
{
static if (T.length > 1)
{
static if (is(typeof(T[$-1]) : string))
{
alias ReverseTupleSpecs = AliasSeq!(T[$-2], T[$-1], ReverseTupleSpecs!(T[0 .. $-2]));
}
else
{
alias ReverseTupleSpecs = AliasSeq!(T[$-1], ReverseTupleSpecs!(T[0 .. $-1]));
}
}
else
{
alias ReverseTupleSpecs = T;
}
}
// ensure that internal Tuple unittests are compiled
@safe unittest
{
Tuple!() t;
}
@safe unittest
{
import std.conv;
{
Tuple!(int, "a", int, "b") nosh;
static assert(nosh.length == 2);
nosh.a = 5;
nosh.b = 6;
assert(nosh.a == 5);
assert(nosh.b == 6);
}
{
Tuple!(short, double) b;
static assert(b.length == 2);
b[1] = 5;
auto a = Tuple!(int, real)(b);
assert(a[0] == 0 && a[1] == 5);
a = Tuple!(int, real)(1, 2);
assert(a[0] == 1 && a[1] == 2);
auto c = Tuple!(int, "a", double, "b")(a);
assert(c[0] == 1 && c[1] == 2);
}
{
Tuple!(int, real) nosh;
nosh[0] = 5;
nosh[1] = 0;
assert(nosh[0] == 5 && nosh[1] == 0);
assert(nosh.to!string == "Tuple!(int, real)(5, 0)", nosh.to!string);
Tuple!(int, int) yessh;
nosh = yessh;
}
{
class A {}
Tuple!(int, shared A) nosh;
nosh[0] = 5;
assert(nosh[0] == 5 && nosh[1] is null);
assert(nosh.to!string == "Tuple!(int, shared(A))(5, shared(A))");
}
{
Tuple!(int, string) t;
t[0] = 10;
t[1] = "str";
assert(t[0] == 10 && t[1] == "str");
assert(t.to!string == `Tuple!(int, string)(10, "str")`, t.to!string);
}
{
Tuple!(int, "a", double, "b") x;
static assert(x.a.offsetof == x[0].offsetof);
static assert(x.b.offsetof == x[1].offsetof);
x.b = 4.5;
x.a = 5;
assert(x[0] == 5 && x[1] == 4.5);
assert(x.a == 5 && x.b == 4.5);
}
// indexing
{
Tuple!(int, real) t;
static assert(is(typeof(t[0]) == int));
static assert(is(typeof(t[1]) == real));
int* p0 = &t[0];
real* p1 = &t[1];
t[0] = 10;
t[1] = -200.0L;
assert(*p0 == t[0]);
assert(*p1 == t[1]);
}
// slicing
{
Tuple!(int, "x", real, "y", double, "z", string) t;
t[0] = 10;
t[1] = 11;
t[2] = 12;
t[3] = "abc";
auto a = t.slice!(0, 3);
assert(a.length == 3);
assert(a.x == t.x);
assert(a.y == t.y);
assert(a.z == t.z);
auto b = t.slice!(2, 4);
assert(b.length == 2);
assert(b.z == t.z);
assert(b[1] == t[3]);
}
// nesting
{
Tuple!(Tuple!(int, real), Tuple!(string, "s")) t;
static assert(is(typeof(t[0]) == Tuple!(int, real)));
static assert(is(typeof(t[1]) == Tuple!(string, "s")));
static assert(is(typeof(t[0][0]) == int));
static assert(is(typeof(t[0][1]) == real));
static assert(is(typeof(t[1].s) == string));
t[0] = tuple(10, 20.0L);
t[1].s = "abc";
assert(t[0][0] == 10);
assert(t[0][1] == 20.0L);
assert(t[1].s == "abc");
}
// non-POD
{
static struct S
{
int count;
this(this) { ++count; }
~this() { --count; }
void opAssign(S rhs) { count = rhs.count; }
}
Tuple!(S, S) ss;
Tuple!(S, S) ssCopy = ss;
assert(ssCopy[0].count == 1);
assert(ssCopy[1].count == 1);
ssCopy[1] = ssCopy[0];
assert(ssCopy[1].count == 2);
}
// https://issues.dlang.org/show_bug.cgi?id=2800
{
static struct R
{
Tuple!(int, int) _front;
@property ref Tuple!(int, int) front() return { return _front; }
@property bool empty() { return _front[0] >= 10; }
void popFront() { ++_front[0]; }
}
foreach (a; R())
{
static assert(is(typeof(a) == Tuple!(int, int)));
assert(0 <= a[0] && a[0] < 10);
assert(a[1] == 0);
}
}
// Construction with compatible elements
{
auto t1 = Tuple!(int, double)(1, 1);
// https://issues.dlang.org/show_bug.cgi?id=8702
auto t8702a = tuple(tuple(1));
auto t8702b = Tuple!(Tuple!(int))(Tuple!(int)(1));
}
// Construction with compatible tuple
{
Tuple!(int, int) x;
x[0] = 10;
x[1] = 20;
Tuple!(int, "a", double, "b") y = x;
assert(y.a == 10);
assert(y.b == 20);
// incompatible
static assert(!__traits(compiles, Tuple!(int, int)(y)));
}
// https://issues.dlang.org/show_bug.cgi?id=6275
{
const int x = 1;
auto t1 = tuple(x);
alias T = Tuple!(const(int));
auto t2 = T(1);
}
// https://issues.dlang.org/show_bug.cgi?id=9431
{
alias T = Tuple!(int[1][]);
auto t = T([[10]]);
}
// https://issues.dlang.org/show_bug.cgi?id=7666
{
auto tup = tuple(1, "2");
assert(tup.reverse == tuple("2", 1));
}
{
Tuple!(int, "x", string, "y") tup = tuple(1, "2");
auto rev = tup.reverse;
assert(rev == tuple("2", 1));
assert(rev.x == 1 && rev.y == "2");
}
{
Tuple!(wchar, dchar, int, "x", string, "y", char, byte, float) tup;
tup = tuple('a', 'b', 3, "4", 'c', cast(byte) 0x0D, 0.00);
auto rev = tup.reverse;
assert(rev == tuple(0.00, cast(byte) 0x0D, 'c', "4", 3, 'b', 'a'));
assert(rev.x == 3 && rev.y == "4");
}
}
@safe unittest
{
// opEquals
{
struct Equ1 { bool opEquals(Equ1) { return true; } }
auto tm1 = tuple(Equ1.init);
const tc1 = tuple(Equ1.init);
static assert( is(typeof(tm1 == tm1)));
static assert(!is(typeof(tm1 == tc1)));
static assert(!is(typeof(tc1 == tm1)));
static assert(!is(typeof(tc1 == tc1)));
struct Equ2 { bool opEquals(const Equ2) const { return true; } }
auto tm2 = tuple(Equ2.init);
const tc2 = tuple(Equ2.init);
static assert( is(typeof(tm2 == tm2)));
static assert( is(typeof(tm2 == tc2)));
static assert( is(typeof(tc2 == tm2)));
static assert( is(typeof(tc2 == tc2)));
// https://issues.dlang.org/show_bug.cgi?id=8686
struct Equ3 { bool opEquals(T)(T) { return true; } }
auto tm3 = tuple(Equ3.init);
const tc3 = tuple(Equ3.init);
static assert( is(typeof(tm3 == tm3)));
static assert( is(typeof(tm3 == tc3)));
static assert(!is(typeof(tc3 == tm3)));
static assert(!is(typeof(tc3 == tc3)));
struct Equ4 { bool opEquals(T)(T) const { return true; } }
auto tm4 = tuple(Equ4.init);
const tc4 = tuple(Equ4.init);
static assert( is(typeof(tm4 == tm4)));
static assert( is(typeof(tm4 == tc4)));
static assert( is(typeof(tc4 == tm4)));
static assert( is(typeof(tc4 == tc4)));
}
// opCmp
{
struct Cmp1 { int opCmp(Cmp1) { return 0; } }
auto tm1 = tuple(Cmp1.init);
const tc1 = tuple(Cmp1.init);
static assert( is(typeof(tm1 < tm1)));
static assert(!is(typeof(tm1 < tc1)));
static assert(!is(typeof(tc1 < tm1)));
static assert(!is(typeof(tc1 < tc1)));
struct Cmp2 { int opCmp(const Cmp2) const { return 0; } }
auto tm2 = tuple(Cmp2.init);
const tc2 = tuple(Cmp2.init);
static assert( is(typeof(tm2 < tm2)));
static assert( is(typeof(tm2 < tc2)));
static assert( is(typeof(tc2 < tm2)));
static assert( is(typeof(tc2 < tc2)));
struct Cmp3 { int opCmp(T)(T) { return 0; } }
auto tm3 = tuple(Cmp3.init);
const tc3 = tuple(Cmp3.init);
static assert( is(typeof(tm3 < tm3)));
static assert( is(typeof(tm3 < tc3)));
static assert(!is(typeof(tc3 < tm3)));
static assert(!is(typeof(tc3 < tc3)));
struct Cmp4 { int opCmp(T)(T) const { return 0; } }
auto tm4 = tuple(Cmp4.init);
const tc4 = tuple(Cmp4.init);
static assert( is(typeof(tm4 < tm4)));
static assert( is(typeof(tm4 < tc4)));
static assert( is(typeof(tc4 < tm4)));
static assert( is(typeof(tc4 < tc4)));
}
// https://issues.dlang.org/show_bug.cgi?id=14890
static void test14890(inout int[] dummy)
{
alias V = Tuple!(int, int);
V mv;
const V cv;
immutable V iv;
inout V wv; // OK <- NG
inout const V wcv; // OK <- NG
static foreach (v1; AliasSeq!(mv, cv, iv, wv, wcv))
static foreach (v2; AliasSeq!(mv, cv, iv, wv, wcv))
{
assert(!(v1 < v2));
}
}
{
int[2] ints = [ 1, 2 ];
Tuple!(int, int) t = ints;
assert(t[0] == 1 && t[1] == 2);
Tuple!(long, uint) t2 = ints;
assert(t2[0] == 1 && t2[1] == 2);
}
}
@safe unittest
{
auto t1 = Tuple!(int, "x", string, "y")(1, "a");
assert(t1.x == 1);
assert(t1.y == "a");
void foo(Tuple!(int, string) t2) {}
foo(t1);
Tuple!(int, int)[] arr;
arr ~= tuple(10, 20); // OK
arr ~= Tuple!(int, "x", int, "y")(10, 20); // NG -> OK
static assert(is(typeof(Tuple!(int, "x", string, "y").tupleof) ==
typeof(Tuple!(int, string ).tupleof)));
}
@safe unittest
{
// https://issues.dlang.org/show_bug.cgi?id=10686
immutable Tuple!(int) t1;
auto r1 = t1[0]; // OK
immutable Tuple!(int, "x") t2;
auto r2 = t2[0]; // error
}
@safe unittest
{
import std.exception : assertCTFEable;
// https://issues.dlang.org/show_bug.cgi?id=10218
assertCTFEable!(
{
auto t = tuple(1);
t = tuple(2); // assignment
});
}
@safe unittest
{
class Foo{}
Tuple!(immutable(Foo)[]) a;
}
@safe unittest
{
//Test non-assignable
static struct S
{
int* p;
}
alias IS = immutable S;
static assert(!isAssignable!IS);
auto s = IS.init;
alias TIS = Tuple!IS;
TIS a = tuple(s);
TIS b = a;
alias TISIS = Tuple!(IS, IS);
TISIS d = tuple(s, s);
IS[2] ss;
TISIS e = TISIS(ss);
}
// https://issues.dlang.org/show_bug.cgi?id=9819
@safe unittest
{
alias T = Tuple!(int, "x", double, "foo");
static assert(T.fieldNames[0] == "x");
static assert(T.fieldNames[1] == "foo");
alias Fields = Tuple!(int, "id", string, float);
static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
}
// https://issues.dlang.org/show_bug.cgi?id=13837
@safe unittest
{
// New behaviour, named arguments.
static assert(is(
typeof(tuple!("x")(1)) == Tuple!(int, "x")));
static assert(is(
typeof(tuple!("x")(1.0)) == Tuple!(double, "x")));
static assert(is(
typeof(tuple!("x")("foo")) == Tuple!(string, "x")));
static assert(is(
typeof(tuple!("x", "y")(1, 2.0)) == Tuple!(int, "x", double, "y")));
auto a = tuple!("a", "b", "c")("1", 2, 3.0f);
static assert(is(typeof(a.a) == string));
static assert(is(typeof(a.b) == int));
static assert(is(typeof(a.c) == float));
// Old behaviour, but with explicit type parameters.
static assert(is(
typeof(tuple!(int, double)(1, 2.0)) == Tuple!(int, double)));
static assert(is(
typeof(tuple!(const int)(1)) == Tuple!(const int)));
static assert(is(
typeof(tuple()) == Tuple!()));
// Nonsensical behaviour
static assert(!__traits(compiles, tuple!(1)(2)));
static assert(!__traits(compiles, tuple!("x")(1, 2)));
static assert(!__traits(compiles, tuple!("x", "y")(1)));
static assert(!__traits(compiles, tuple!("x")()));
static assert(!__traits(compiles, tuple!("x", int)(2)));
}
@safe unittest
{
class C { override size_t toHash() const nothrow @safe { return 0; } }
Tuple!(Rebindable!(const C)) a;
Tuple!(const C) b;
a = b;
}
@nogc @safe unittest
{
alias T = Tuple!(string, "s");
T x;
x = T.init;
}
@safe unittest
{
import std.format : format, FormatException;
import std.exception : assertThrown;
//enum tupStr = tuple(1, 1.0).toString; // toString is *impure*.
//static assert(tupStr == `Tuple!(int, double)(1, 1)`);
}
// https://issues.dlang.org/show_bug.cgi?id=17803, parte uno
@safe unittest
{
auto a = tuple(3, "foo");
assert(__traits(compiles, { a = (a = a); }));
}
// Ditto
@safe unittest
{
Tuple!(int[]) a, b, c;
a = tuple([0, 1, 2]);
c = b = a;
assert(a[0].length == b[0].length && b[0].length == c[0].length);
assert(a[0].ptr == b[0].ptr && b[0].ptr == c[0].ptr);
}
/**
Constructs a $(LREF Tuple) object instantiated and initialized according to
the given arguments.
Params:
Names = An optional list of strings naming each successive field of the `Tuple`
or a list of types that the elements are being casted to.
For a list of names,
each name matches up with the corresponding field given by `Args`.
A name does not have to be provided for every field, but as
the names must proceed in order, it is not possible to skip
one field and name the next after it.
For a list of types,
there must be exactly as many types as parameters.
*/
template tuple(Names...)
{
/**
Params:
args = Values to initialize the `Tuple` with. The `Tuple`'s type will
be inferred from the types of the values given.
Returns:
A new `Tuple` with its type inferred from the arguments given.
*/
auto tuple(Args...)(Args args)
{
static if (Names.length == 0)
{
// No specified names, just infer types from Args...
return Tuple!Args(args);
}
else static if (!is(typeof(Names[0]) : string))
{
// Names[0] isn't a string, must be explicit types.
return Tuple!Names(args);
}
else
{
// Names[0] is a string, so must be specifying names.
static assert(Names.length == Args.length,
"Insufficient number of names given.");
// Interleave(a, b).and(c, d) == (a, c, b, d)
// This is to get the interleaving of types and names for Tuple
// e.g. Tuple!(int, "x", string, "y")
template Interleave(A...)
{
template and(B...) if (B.length == 1)
{
alias and = AliasSeq!(A[0], B[0]);
}
template and(B...) if (B.length != 1)
{
alias and = AliasSeq!(A[0], B[0],
Interleave!(A[1..$]).and!(B[1..$]));
}
}
return Tuple!(Interleave!(Args).and!(Names))(args);
}
}
}
///
@safe unittest
{
auto value = tuple(5, 6.7, "hello");
assert(value[0] == 5);
assert(value[1] == 6.7);
assert(value[2] == "hello");
// Field names can be provided.
auto entry = tuple!("index", "value")(4, "Hello");
assert(entry.index == 4);
assert(entry.value == "Hello");
}
/**
Returns `true` if and only if `T` is an instance of `std.typecons.Tuple`.
Params:
T = The type to check.
Returns:
true if `T` is a `Tuple` type, false otherwise.
*/
enum isTuple(T) = __traits(compiles,
{
void f(Specs...)(Tuple!Specs tup) {}
f(T.init);
} );
///
@safe unittest
{
static assert(isTuple!(Tuple!()));
static assert(isTuple!(Tuple!(int)));
static assert(isTuple!(Tuple!(int, real, string)));
static assert(isTuple!(Tuple!(int, "x", real, "y")));
static assert(isTuple!(Tuple!(int, Tuple!(real), string)));
}
@safe unittest
{
static assert(isTuple!(const Tuple!(int)));
static assert(isTuple!(immutable Tuple!(int)));
static assert(!isTuple!(int));
static assert(!isTuple!(const int));
struct S {}
static assert(!isTuple!(S));
}
// used by both Rebindable and UnqualRef
private mixin template RebindableCommon(T, U, alias This)
if (is(T == class) || is(T == interface) || isAssociativeArray!T)
{
private union
{
T original;
U stripped;
}
void opAssign(return scope T another) pure nothrow @nogc
{
// If `T` defines `opCast` we must infer the safety
static if (hasMember!(T, "opCast"))
{
// This will allow the compiler to infer the safety of `T.opCast!U`
// without generating any runtime cost
if (false) { stripped = cast(U) another; }
}
() @trusted { stripped = cast(U) another; }();
}
void opAssign(typeof(this) another) @trusted pure nothrow @nogc
{
stripped = another.stripped;
}
static if (is(T == const U) && is(T == const shared U))
{
// safely assign immutable to const / const shared
void opAssign(This!(immutable U) another) @trusted pure nothrow @nogc
{
stripped = another.stripped;
}
}
this(T initializer) pure nothrow @nogc
{
// Infer safety from opAssign
opAssign(initializer);
}
@property inout(T) get() @trusted pure nothrow @nogc return scope inout
{
return original;
}
bool opEquals()(auto ref const(typeof(this)) rhs) const
{
// Must forward explicitly because 'stripped' is part of a union.
// The necessary 'toHash' is forwarded to the class via alias this.
return stripped == rhs.stripped;
}
bool opEquals(const(U) rhs) const
{
return stripped == rhs;
}
alias get this;
}
/**
`Rebindable!(T)` is a simple, efficient wrapper that behaves just
like an object of type `T`, except that you can reassign it to
refer to another object. For completeness, `Rebindable!(T)` aliases
itself away to `T` if `T` is a non-const object type.
You may want to use `Rebindable` when you want to have mutable
storage referring to `const` objects, for example an array of
references that must be sorted in place. `Rebindable` does not
break the soundness of D's type system and does not incur any of the
risks usually associated with `cast`.
Params:
T = An object, interface, array slice type, or associative array type.
*/
template Rebindable(T)
if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
{
static if (is(T == const U, U) || is(T == immutable U, U))
{
static if (isDynamicArray!T)
{
import std.range.primitives : ElementEncodingType;
alias Rebindable = const(ElementEncodingType!T)[];
}
else
{
struct Rebindable
{
mixin RebindableCommon!(T, U, Rebindable);
}
}
}
else
{
alias Rebindable = T;
}
}
///Regular `const` object references cannot be reassigned.
@safe unittest
{
class Widget { int x; int y() @safe const { return x; } }
const a = new Widget;
// Fine
a.y();
// error! can't modify const a
// a.x = 5;
// error! can't modify const a
// a = new Widget;
}
/**
However, `Rebindable!(Widget)` does allow reassignment,
while otherwise behaving exactly like a $(D const Widget).
*/
@safe unittest
{
class Widget { int x; int y() const @safe { return x; } }
auto a = Rebindable!(const Widget)(new Widget);
// Fine
a.y();
// error! can't modify const a
// a.x = 5;
// Fine
a = new Widget;
}
// https://issues.dlang.org/show_bug.cgi?id=16054
@safe unittest
{
Rebindable!(immutable Object) r;
static assert(__traits(compiles, r.get()));
static assert(!__traits(compiles, &r.get()));
}
@safe unittest
{
class CustomToHash
{
override size_t toHash() const nothrow @trusted { return 42; }
}
Rebindable!(immutable(CustomToHash)) a = new immutable CustomToHash();
assert(a.toHash() == 42, "Rebindable!A should offer toHash()"
~ " by forwarding to A.toHash().");
}
// https://issues.dlang.org/show_bug.cgi?id=18615
// Rebindable!A should use A.opEqualsa
@system unittest
{
class CustomOpEq
{
int x;
override bool opEquals(Object rhsObj)
{
if (auto rhs = cast(const(CustomOpEq)) rhsObj)
return this.x == rhs.x;
else
return false;
}
}
CustomOpEq a = new CustomOpEq();
CustomOpEq b = new CustomOpEq();
assert(a !is b);
assert(a == b, "a.x == b.x should be true (0 == 0).");
Rebindable!(const(CustomOpEq)) ra = a;
Rebindable!(const(CustomOpEq)) rb = b;
assert(ra !is rb);
assert(ra == rb, "Rebindable should use CustomOpEq's opEquals, not 'is'.");
assert(ra == b, "Rebindable!(someQualifier(A)) should be comparable"
~ " against const(A) via A.opEquals.");
assert(a == rb, "Rebindable!(someQualifier(A)) should be comparable"
~ " against const(A) via A.opEquals.");
b.x = 1;
assert(a != b);
assert(ra != b, "Rebindable!(someQualifier(A)) should be comparable"
~ " against const(A) via A.opEquals.");
assert(a != rb, "Rebindable!(someQualifier(A)) should be comparable"
~ " against const(A) via A.opEquals.");
Rebindable!(const(Object)) o1 = new Object();
Rebindable!(const(Object)) o2 = new Object();
assert(o1 !is o2);
assert(o1 == o1, "When the class doesn't provide its own opEquals,"
~ " Rebindable treats 'a == b' as 'a is b' like Object.opEquals.");
assert(o1 != o2, "When the class doesn't provide its own opEquals,"
~ " Rebindable treats 'a == b' as 'a is b' like Object.opEquals.");
assert(o1 != new Object(), "Rebindable!(const(Object)) should be"
~ " comparable against Object itself and use Object.opEquals.");
}
// https://issues.dlang.org/show_bug.cgi?id=18755
@safe unittest
{
static class Foo
{
auto opCast(T)() @system immutable pure nothrow
{
*(cast(uint*) 0xdeadbeef) = 0xcafebabe;
return T.init;
}
}
static assert(!__traits(compiles, () @safe {
auto r = Rebindable!(immutable Foo)(new Foo);
}));
static assert(__traits(compiles, () @system {
auto r = Rebindable!(immutable Foo)(new Foo);
}));
}
/**
Convenience function for creating a `Rebindable` using automatic type
inference.
Params:
obj = A reference to an object, interface, associative array, or an array slice
to initialize the `Rebindable` with.
Returns:
A newly constructed `Rebindable` initialized with the given reference.
*/
Rebindable!T rebindable(T)(T obj)
if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
{
typeof(return) ret;
ret = obj;
return ret;
}
///
@system unittest
{
class C
{
int payload;
this(int p) { payload = p; }
}
const c = new C(1);
auto c2 = c.rebindable;
assert(c2.payload == 1);
// passing Rebindable to rebindable
c2 = c2.rebindable;
c2 = new C(2);
assert(c2.payload == 2);
const c3 = c2.get;
assert(c3.payload == 2);
}
/**
This function simply returns the `Rebindable` object passed in. It's useful
in generic programming cases when a given object may be either a regular
`class` or a `Rebindable`.
Params:
obj = An instance of Rebindable!T.
Returns:
`obj` without any modification.
*/
Rebindable!T rebindable(T)(Rebindable!T obj)
{
return obj;
}
// TODO: remove me once the rebindable overloads have been joined
///
@system unittest
{
class C
{
int payload;
this(int p) { payload = p; }
}
const c = new C(1);
auto c2 = c.rebindable;
assert(c2.payload == 1);
// passing Rebindable to rebindable
c2 = c2.rebindable;
assert(c2.payload == 1);
}
@system unittest
{
interface CI { int foo() const; }
class C : CI {
int foo() const { return 42; }
@property int bar() const { return 23; }
}
Rebindable!(C) obj0;
static assert(is(typeof(obj0) == C));
Rebindable!(const(C)) obj1;
static assert(is(typeof(obj1.get) == const(C)), typeof(obj1.get).stringof);
static assert(is(typeof(obj1.stripped) == C));
obj1 = new C;
assert(obj1.get !is null);
obj1 = new const(C);
assert(obj1.get !is null);
Rebindable!(immutable(C)) obj2;
static assert(is(typeof(obj2.get) == immutable(C)));
static assert(is(typeof(obj2.stripped) == C));
obj2 = new immutable(C);
assert(obj1.get !is null);
// test opDot
assert(obj2.foo() == 42);
assert(obj2.bar == 23);
interface I { final int foo() const { return 42; } }
Rebindable!(I) obj3;
static assert(is(typeof(obj3) == I));
Rebindable!(const I) obj4;
static assert(is(typeof(obj4.get) == const I));
static assert(is(typeof(obj4.stripped) == I));
static assert(is(typeof(obj4.foo()) == int));
obj4 = new class I {};
Rebindable!(immutable C) obj5i;
Rebindable!(const C) obj5c;
obj5c = obj5c;
obj5c = obj5i;
obj5i = obj5i;
static assert(!__traits(compiles, obj5i = obj5c));
// Test the convenience functions.
auto obj5convenience = rebindable(obj5i);
assert(obj5convenience is obj5i);
auto obj6 = rebindable(new immutable(C));
static assert(is(typeof(obj6) == Rebindable!(immutable C)));
assert(obj6.foo() == 42);
auto obj7 = rebindable(new C);
CI interface1 = obj7;
auto interfaceRebind1 = rebindable(interface1);
assert(interfaceRebind1.foo() == 42);
const interface2 = interface1;
auto interfaceRebind2 = rebindable(interface2);
assert(interfaceRebind2.foo() == 42);
auto arr = [1,2,3,4,5];
const arrConst = arr;
assert(rebindable(arr) == arr);
assert(rebindable(arrConst) == arr);
// https://issues.dlang.org/show_bug.cgi?id=7654
immutable(char[]) s7654;
Rebindable!(typeof(s7654)) r7654 = s7654;
static foreach (T; AliasSeq!(char, wchar, char, int))
{
static assert(is(Rebindable!(immutable(T[])) == immutable(T)[]));
static assert(is(Rebindable!(const(T[])) == const(T)[]));
static assert(is(Rebindable!(T[]) == T[]));
}
// https://issues.dlang.org/show_bug.cgi?id=12046
static assert(!__traits(compiles, Rebindable!(int[1])));
static assert(!__traits(compiles, Rebindable!(const int[1])));
// Pull request 3341
Rebindable!(immutable int[int]) pr3341 = [123:345];
assert(pr3341[123] == 345);
immutable int[int] pr3341_aa = [321:543];
pr3341 = pr3341_aa;
assert(pr3341[321] == 543);
assert(rebindable(pr3341_aa)[321] == 543);
}
/**
Similar to `Rebindable!(T)` but strips all qualifiers from the reference as
opposed to just constness / immutability. Primary intended use case is with
shared (having thread-local reference to shared class data)
Params:
T = A class or interface type.
*/
template UnqualRef(T)
if (is(T == class) || is(T == interface))
{
static if (is(T == immutable U, U)
|| is(T == const shared U, U)
|| is(T == const U, U)
|| is(T == shared U, U))
{
struct UnqualRef
{
mixin RebindableCommon!(T, U, UnqualRef);
}
}
else
{
alias UnqualRef = T;
}
}
///
@system unittest
{
class Data {}
static shared(Data) a;
static UnqualRef!(shared Data) b;
import core.thread;
auto thread = new core.thread.Thread({
a = new shared Data();
b = new shared Data();
});
thread.start();
thread.join();
assert(a !is null);
assert(b is null);
}
@safe unittest
{
class C { }
alias T = UnqualRef!(const shared C);
static assert(is(typeof(T.stripped) == C));
}
/**
Order the provided members to minimize size while preserving alignment.
Alignment is not always optimal for 80-bit reals, nor for structs declared
as align(1).
Params:
E = A list of the types to be aligned, representing fields
of an aggregate such as a `struct` or `class`.
names = The names of the fields that are to be aligned.
Returns:
A string to be mixed in to an aggregate, such as a `struct` or `class`.
*/
string alignForSize(E...)(const char[][] names...)
{
// Sort all of the members by .alignof.
// BUG: Alignment is not always optimal for align(1) structs
// or 80-bit reals or 64-bit primitives on x86.
// TRICK: Use the fact that .alignof is always a power of 2,
// and maximum 16 on extant systems. Thus, we can perform
// a very limited radix sort.
// Contains the members with .alignof = 64,32,16,8,4,2,1
assert(E.length == names.length,
"alignForSize: There should be as many member names as the types");
string[7] declaration = ["", "", "", "", "", "", ""];
foreach (i, T; E)
{
auto a = T.alignof;
auto k = a >= 64? 0 : a >= 32? 1 : a >= 16? 2 : a >= 8? 3 : a >= 4? 4 : a >= 2? 5 : 6;
declaration[k] ~= T.stringof ~ " " ~ names[i] ~ ";\n";
}
auto s = "";
foreach (decl; declaration)
s ~= decl;
return s;
}
///
@safe unittest
{
struct Banner {
mixin(alignForSize!(byte[6], double)(["name", "height"]));
}
}
@safe unittest
{
enum x = alignForSize!(int[], char[3], short, double[5])("x", "y","z", "w");
struct Foo { int x; }
enum y = alignForSize!(ubyte, Foo, double)("x", "y", "z");
enum passNormalX = x == "double[5] w;\nint[] x;\nshort z;\nchar[3] y;\n";
enum passNormalY = y == "double z;\nFoo y;\nubyte x;\n";
enum passAbnormalX = x == "int[] x;\ndouble[5] w;\nshort z;\nchar[3] y;\n";
enum passAbnormalY = y == "Foo y;\ndouble z;\nubyte x;\n";
// ^ blame https://issues.dlang.org/show_bug.cgi?id=231
static assert(passNormalX || passAbnormalX && double.alignof <= (int[]).alignof);
static assert(passNormalY || passAbnormalY && double.alignof <= int.alignof);
}
// https://issues.dlang.org/show_bug.cgi?id=12914
@safe unittest
{
immutable string[] fieldNames = ["x", "y"];
struct S
{
mixin(alignForSize!(byte, int)(fieldNames));
}
}
/**
Defines a value paired with a distinctive "null" state that denotes
the absence of a value. If default constructed, a $(D
Nullable!T) object starts in the null state. Assigning it renders it
non-null. Calling `nullify` can nullify it again.
Practically `Nullable!T` stores a `T` and a `bool`.
*/
struct Nullable(T)
{
private union DontCallDestructorT
{
import std.traits : hasIndirections;
static if (hasIndirections!T)
T payload;
else
T payload = void;
}
private DontCallDestructorT _value = DontCallDestructorT.init;
private bool _isNull = true;
/**
* Constructor initializing `this` with `value`.
*
* Params:
* value = The value to initialize this `Nullable` with.
*/
this(inout T value) inout
{
_value.payload = value;
_isNull = false;
}
static if (hasElaborateDestructor!T)
{
~this()
{
if (!_isNull)
{
destroy(_value.payload);
}
}
}
static if (__traits(hasPostblit, T))
{
this(this)
{
if (!_isNull)
_value.payload.__xpostblit();
}
}
else static if (__traits(hasCopyConstructor, T))
{
this(ref return scope inout Nullable!T rhs) inout
{
_isNull = rhs._isNull;
if (!_isNull)
_value.payload = rhs._value.payload;
else
_value = DontCallDestructorT.init;
}
}
/**
* If they are both null, then they are equal. If one is null and the other
* is not, then they are not equal. If they are both non-null, then they are
* equal if their values are equal.
*/
bool opEquals(this This, Rhs)(auto ref Rhs rhs)
if (!is(CommonType!(This, Rhs) == void))
{
static if (is(This == Rhs))
{
if (_isNull)
return rhs._isNull;
if (rhs._isNull)
return false;
return _value.payload == rhs._value.payload;
}
else
{
alias Common = CommonType!(This, Rhs);
return cast(Common) this == cast(Common) rhs;
}
}
/// Ditto
bool opEquals(this This, Rhs)(auto ref Rhs rhs)
if (is(CommonType!(This, Rhs) == void) && is(typeof(this.get == rhs)))
{
return _isNull ? false : rhs == _value.payload;
}
///
@safe unittest
{
Nullable!int empty;
Nullable!int a = 42;
Nullable!int b = 42;
Nullable!int c = 27;
assert(empty == empty);
assert(empty == Nullable!int.init);
assert(empty != a);
assert(empty != b);