Permalink
Fetching contributors…
Cannot retrieve contributors at this time
7813 lines (6898 sloc) 213 KB
// 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.
Source: $(PHOBOSSRC std/_typecons.d)
Synopsis:
----
// value tuples
alias Coord = Tuple!(float, "x", float, "y", float, "z");
Coord c;
c[1] = 1; // access by index
c.z = 1; // access by given name
alias DicEntry = Tuple!(string, string); // names can be omitted
// Rebindable references to const and immutable objects
void bar()
{
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;
}
----
Copyright: Copyright the respective authors, 2008-
License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(HTTP erdani.org, Andrei Alexandrescu),
$(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski),
Don Clugston,
Shin Fujishiro,
Kenji Hara
*/
module std.typecons;
import core.stdc.stdint : uintptr_t;
import std.meta; // : AliasSeq, allSatisfy;
import std.traits;
debug(Unique) import std.stdio;
/**
Encapsulates unique ownership of a resource.
When a $(D Unique!T) goes out of scope it will call $(D destroy)
on the resource $(D T) that it manages, unless it is transferred.
One important consequence of $(D destroy) is that it will call the
destructor of the resource $(D T). GC-managed references are not
guaranteed to be valid during a destructor call, but other members of
$(D T), such as file handles or pointers to $(D malloc) memory, will
still be valid during the destructor call. This allows the resource
$(D T) to deallocate or clean up any non-GC resources.
If it is desirable to persist a $(D Unique!T) outside of its original
scope, then it can be transferred. The transfer can be explicit, by
calling $(D release), or implicit, when returning Unique from a
function. The resource $(D T) can be a polymorphic class object, in
which case Unique behaves polymorphically too.
If $(D T) is a value type, then $(D Unique!T) will be implemented
as a reference to a $(D T).
*/
struct Unique(T)
{
/** Represents a reference to $(D T). Resolves to $(D T*) if $(D T) is a value type. */
static if (is(T:Object))
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 $(D Unique). It creates the resource and
guarantees unique ownership of it (unless $(D T) publishes aliases of
$(D this)).
Note: Nested structs/classes cannot be created.
Params:
args = Arguments to pass to $(D 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)))
{
debug(Unique) writeln("Unique.create for ", T.stringof);
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)
{
debug(Unique) writeln("Unique constructor with rvalue");
_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;
debug(Unique) writeln("Unique constructor nulling source");
p = null;
assert(p is null);
}
/**
Constructor that takes a $(D Unique) of a type that is convertible to our type.
Typically used to transfer a $(D Unique) rvalue of derived type to
a $(D 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))
{
debug(Unique) writeln("Unique constructor converting from ", U.stringof);
_p = u._p;
u._p = null;
}
/// Transfer ownership from a $(D Unique) of a type that is convertible to our type.
void opAssign(U)(Unique!U u)
if (is(u.RefT:RefT))
{
debug(Unique) writeln("Unique opAssign converting from ", U.stringof);
// first delete any resource we own
destroy(this);
_p = u._p;
u._p = null;
}
~this()
{
debug(Unique) writeln("Unique destructor of ", (_p is null)? null: _p);
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 $(D Unique) rvalue. Nullifies the current contents.
Same as calling std.algorithm.move on it.
*/
Unique release()
{
debug(Unique) writeln("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 $(D Unique) objects.
*/
@disable this(this);
private:
RefT _p;
}
///
@system 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
{
debug(Unique) writeln("Unique class");
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);}));
debug(Unique) writeln("Calling g");
auto ub2 = g(ub.release);
debug(Unique) writeln("Returned from g");
assert(ub.isEmpty);
assert(!ub2.isEmpty);
}
@system unittest
{
debug(Unique) writeln("Unique struct");
struct Foo
{
~this() { debug(Unique) writeln(" Foo destructor"); }
int val() const { return 3; }
@disable this(this);
}
alias UFoo = Unique!(Foo);
UFoo f(UFoo u)
{
debug(Unique) writeln("inside f");
return u.release;
}
auto uf = UFoo(new Foo);
assert(!uf.isEmpty);
assert(uf.val == 3);
static assert(!__traits(compiles, {auto uf3 = f(uf);}));
debug(Unique) writeln("Unique struct: calling f");
auto uf2 = f(uf.release);
debug(Unique) writeln("Unique struct: returned from f");
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;
}
/**
_Tuple of values, for example $(D Tuple!(int, string)) is a record that
stores an $(D int) and a $(D string). $(D Tuple) can be used to bundle
values together, notably when returning multiple values from a
function. If $(D obj) is a `Tuple`, the individual members are
accessible with the syntax $(D obj[0]) for the first field, $(D obj[1])
for the second, and so on.
The choice of zero-based indexing instead of one-base indexing was
motivated by the ability to use value tuples with various compile-time
loop constructs (e.g. $(REF AliasSeq, std,meta) iteration), all of which use
zero-based indexing.
See_Also: $(LREF tuple).
Params:
Specs = A list of types (and optionally, member names) that the `Tuple` contains.
*/
template Tuple(Specs...)
{
import std.meta : staticMap;
// Parse (type,name) pairs (FieldSpecs) out of the specified
// arguments. Some fields would have name, others not.
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);
}
}
template FieldSpec(T, string s = "")
{
alias Type = T;
alias name = s;
}
alias fieldSpecs = parseSpecs!Specs;
// Used with staticMap.
alias extractType(alias spec) = spec.Type;
alias extractName(alias spec) = spec.name;
// 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.
string injectNamedFields()
{
string decl = "";
foreach (i, name; staticMap!(extractName, fieldSpecs))
{
import std.format : format;
decl ~= format("alias _%s = Identity!(field[%s]);", i, i);
if (name.length != 0)
{
decl ~= format("alias %s = _%s;", name, i);
}
}
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]);
template expandSpec(alias spec)
{
static if (spec.name.length == 0)
{
alias expandSpec = AliasSeq!(spec.Type);
}
else
{
alias expandSpec = AliasSeq!(spec.Type, spec.name);
}
}
enum areCompatibleTuples(Tup1, Tup2, string op) = isTuple!Tup2 && is(typeof(
(ref Tup1 tup1, ref Tup2 tup2)
{
static assert(tup1.field.length == tup2.field.length);
foreach (i, _; Tup1.Types)
{
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");
}
}));
enum areBuildCompatibleTuples(Tup1, Tup2) = isTuple!Tup2 && is(typeof(
{
static assert(Tup1.Types.length == Tup2.Types.length);
foreach (i, _; Tup1.Types)
static assert(isBuildable!(Tup1.Types[i], Tup2.Types[i]));
}));
/+ Returns $(D true) iff a $(D T) can be initialized from a $(D U). +/
enum isBuildable(T, U) = is(typeof(
{
U u = U.init;
T t = u;
}));
/+ Helper for partial instanciation +/
template isBuildableFrom(U)
{
enum isBuildableFrom(T) = isBuildable!(T, U);
}
struct Tuple
{
/**
* The types of the `Tuple`'s components.
*/
alias Types = staticMap!(extractType, fieldSpecs);
///
static if (Specs.length == 0) @safe unittest
{
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
{
alias Fields = Tuple!(int, "id", string, float);
static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
}
/**
* Use $(D t.expand) for a `Tuple` $(D t) to expand it into its
* components. The result of $(D expand) acts as if the `Tuple`'s components
* were listed as a list of values. (Ordinarily, a $(D Tuple) acts as a
* single value.)
*/
Types expand;
mixin(injectNamedFields());
///
static if (Specs.length == 0) @safe unittest
{
auto t1 = tuple(1, " hello ", 2.3);
assert(t1.toString() == `Tuple!(int, string, double)(1, " hello ", 2.3)`);
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
{
foreach (i, _; Types) // 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))
{
foreach (i, _; Types)
{
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[];
}
///
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` on the right-hand side and `v2` on the
* left-hand side:
*
* $(UL
* $(LI A negative integer if the expression `v1 < v2` is true.)
* $(LI A positive integer if the expression `v1 > v2` is true.)
* $(LI 0 if the expression `v1 == v2` is true.))
*/
int opCmp(R)(R rhs)
if (areCompatibleTuples!(typeof(this), R, "<"))
{
foreach (i, Unused; Types)
{
if (field[i] != rhs.field[i])
{
return field[i] < rhs.field[i] ? -1 : 1;
}
}
return 0;
}
/// ditto
int opCmp(R)(R rhs) const
if (areCompatibleTuples!(typeof(this), R, "<"))
{
foreach (i, Unused; Types)
{
if (field[i] != rhs.field[i])
{
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);
}
/**
* 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`.
*/
void 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))
{
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[];
}
}
/**
* 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...)() return
if (names.length == 0 || allSatisfy!(isSomeString, typeof(names)))
{
import std.algorithm.comparison : equal;
// to circumvent bug 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 .. $]);
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(!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);
}
/**
* 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)()
if (is(typeof(translate) : V[K], V, K) && isSomeString!V &&
(isSomeString!K || is(K : size_t)))
{
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);
}
///
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 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 Tuple!(sliceSpecs!(from, to)) slice(size_t from, size_t to)() @trusted
if (from <= to && to <= Types.length)
{
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);
}
/**
Creates a hash of this `Tuple`.
Returns:
A `size_t` representing the hash of this `Tuple`.
*/
size_t toHash() const nothrow @trusted
{
size_t h = 0;
foreach (i, T; Types)
h += typeid(T).getHash(cast(const void*)&field[i]);
return h;
}
///
template toString()
{
/**
* 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 : 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`, so
* it may contain as many formats as the `Tuple` has fields.))
* $(TROW $(P `%(inner%|sep%)`), $(P The format `inner` is one format, that is applied
* on all fields of the `Tuple`. The inner format must be compatible to all
* of them.)))
* ---
* 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`);
*
*
* // 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`
* );
* ---
*/
void toString(DG)(scope DG sink) const
{
toString(sink, FormatSpec!char());
}
/// ditto
void toString(DG, Char)(scope DG sink, FormatSpec!Char fmt) const
{
import std.format : formatElement, formattedWrite, FormatException;
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 formatElement() works for shared objects.
static if (is(Type == class) && is(Type == shared))
{
sink(Type.stringof);
}
else
{
FormatSpec!Char f;
formatElement(sink, field[i], f);
}
}
sink(footer);
}
else
{
throw new FormatException(
"Expected '%s' or '%(...%)' or '%(...%|...%)' format specifier for type '" ~
Unqual!(typeof(this)).stringof ~ "', not '%" ~ fmt.spec ~ "'.");
}
}
}
}
}
///
@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)));
}
/**
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
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);
}
// bug 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);
// 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)));
}
// 6275
{
const int x = 1;
auto t1 = tuple(x);
alias T = Tuple!(const(int));
auto t2 = T(1);
}
// 9431
{
alias T = Tuple!(int[1][]);
auto t = T([[10]]);
}
// 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)));
struct Equ3 { bool opEquals(T)(T) { return true; } }
auto tm3 = tuple(Equ3.init); // bugzilla 8686
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)));
}
// Bugzilla 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
foreach (v1; AliasSeq!(mv, cv, iv, wv, wcv))
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
{
// Bugzilla 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;
// Bugzilla 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);
}
// Bugzilla #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", "", ""));
}
// Bugzilla 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 {}
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)`);
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`);
// 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`
);
}
/**
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`.
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.
*/
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 $(D true) if and only if $(D T) is an instance of $(D 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;
}
@trusted pure nothrow @nogc
{
void opAssign(T another)
{
stripped = cast(U) another;
}
void opAssign(typeof(this) another)
{
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)
{
stripped = another.stripped;
}
}
this(T initializer)
{
opAssign(initializer);
}
@property inout(T) get() inout
{
return original;
}
}
alias get this;
}
/**
$(D Rebindable!(T)) is a simple, efficient wrapper that behaves just
like an object of type $(D T), except that you can reassign it to
refer to another object. For completeness, $(D Rebindable!(T)) aliases
itself away to $(D T) if $(D T) is a non-const object type.
You may want to use $(D Rebindable) when you want to have mutable
storage referring to $(D const) objects, for example an array of
references that must be sorted in place. $(D Rebindable) does not
break the soundness of D's type system and does not incur any of the
risks usually associated with $(D 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 $(D const) object references cannot be reassigned.
@system unittest
{
class Widget { int x; int y() 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, $(D Rebindable!(Widget)) does allow reassignment,
while otherwise behaving exactly like a $(D const Widget).
*/
@system unittest
{
class Widget { int x; int y() const { 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;
}
@safe unittest // issue 16054
{
Rebindable!(immutable Object) r;
static assert(__traits(compiles, r.get()));
static assert(!__traits(compiles, &r.get()));
}
/**
Convenience function for creating a $(D 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;
}
/**
This function simply returns the $(D Rebindable) object passed in. It's useful
in generic programming cases when a given object may be either a regular
$(D class) or a $(D Rebindable).
Params:
obj = An instance of Rebindable!T.
Returns:
`obj` without any modification.
*/
Rebindable!T rebindable(T)(Rebindable!T obj)
{
return obj;
}
@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);
// Issue 7654
immutable(char[]) s7654;
Rebindable!(typeof(s7654)) r7654 = s7654;
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[]));
}
// Issue 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 $(D 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 == const U, U)
|| is(T == immutable U, U)
|| is(T == shared U, U)
|| is(T == const 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, cdouble)("x", "y", "z");
enum passNormalX = x == "double[5] w;\nint[] x;\nshort z;\nchar[3] y;\n";
enum passNormalY = y == "cdouble 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;\ncdouble z;\nubyte x;\n";
// ^ blame http://d.puremagic.com/issues/show_bug.cgi?id=231
static assert(passNormalX || passAbnormalX && double.alignof <= (int[]).alignof);
static assert(passNormalY || passAbnormalY && double.alignof <= int.alignof);
}
// Issue 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 $(D nullify) can nullify it again.
Practically $(D Nullable!T) stores a $(D T) and a $(D bool).
*/
struct Nullable(T)
{
private T _value;
private bool _isNull = true;
/**
Constructor initializing $(D this) with $(D value).
Params:
value = The value to initialize this `Nullable` with.
*/
this(inout T value) inout
{
_value = value;
_isNull = false;
}
/**
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()(auto ref const(typeof(this)) rhs) const
{
if (_isNull)
return rhs._isNull;
if (rhs._isNull)
return false;
return _value == rhs._value;
}
/// Ditto
bool opEquals()(auto ref const(T) rhs) const
{
return _isNull ? false : rhs == _value;
}
///
@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);
assert(empty != c);
assert(a == b);
assert(a != c);
assert(empty != 42);
assert(a == 42);
assert(c != 42);
}
@safe unittest
{
// Test constness
immutable Nullable!int a = 42;
Nullable!int b = 42;
immutable Nullable!int c = 29;
Nullable!int d = 29;
immutable e = 42;
int f = 29;
assert(a == a);
assert(a == b);
assert(a != c);
assert(a != d);
assert(a == e);
assert(a != f);
// Test rvalue
assert(a == const Nullable!int(42));
assert(a != Nullable!int(29));
}
template toString()
{
import std.format : FormatSpec, formatValue;
// Needs to be a template because of DMD @@BUG@@ 13737.
void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
{
if (isNull)
{
sink.formatValue("Nullable.null", fmt);
}
else
{
sink.formatValue(_value, fmt);
}
}
// Issue 14940
void toString()(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt)
{
if (isNull)
{
sink.formatValue("Nullable.null", fmt);
}
else
{
sink.formatValue(_value, fmt);
}
}
}
/**
Check if `this` is in the null state.
Returns:
true $(B iff) `this` is in the null state, otherwise false.
*/
@property bool isNull() const @safe pure nothrow
{
return _isNull;
}
///
@system unittest
{
Nullable!int ni;
assert(ni.isNull);
ni = 0;
assert(!ni.isNull);
}
// Issue 14940
@safe unittest
{
import std.array : appender;
import std.format : formattedWrite;
auto app = appender!string();
Nullable!int a = 1;
formattedWrite(app, "%s", a);
assert(app.data == "1");
}
/**
Forces $(D this) to the null state.
*/
void nullify()()
{
.destroy(_value);
_isNull = true;
}
///
@safe unittest
{
Nullable!int ni = 0;
assert(!ni.isNull);
ni.nullify();
assert(ni.isNull);
}
/**
Assigns $(D value) to the internally-held state. If the assignment
succeeds, $(D this) becomes non-null.
Params:
value = A value of type `T` to assign to this `Nullable`.
*/
void opAssign()(T value)
{
_value = value;
_isNull = false;
}
/**
If this `Nullable` wraps a type that already has a null value
(such as a pointer), then assigning the null value to this
`Nullable` is no different than assigning any other value of
type `T`, and the resulting code will look very strange. It
is strongly recommended that this be avoided by instead using
the version of `Nullable` that takes an additional `nullValue`
template argument.
*/
@safe unittest
{
//Passes
Nullable!(int*) npi;
assert(npi.isNull);
//Passes?!
npi = null;
assert(!npi.isNull);
}
/**
Gets the value. $(D this) must not be in the null state.
This function is also called for the implicit conversion to $(D T).
Returns:
The value held internally by this `Nullable`.
*/
@property ref inout(T) get() inout @safe pure nothrow
{
enum message = "Called `get' on null Nullable!" ~ T.stringof ~ ".";
assert(!isNull, message);
return _value;
}
///
@system unittest
{
import core.exception : AssertError;
import std.exception : assertThrown, assertNotThrown;
Nullable!int ni;
int i = 42;
//`get` is implicitly called. Will throw
//an AssertError in non-release mode
assertThrown!AssertError(i = ni);
assert(i == 42);
ni = 5;
assertNotThrown!AssertError(i = ni);
assert(i == 5);
}
/**
Implicitly converts to $(D T).
$(D this) must not be in the null state.
*/
alias get this;
}
/// ditto
auto nullable(T)(T t)
{
return Nullable!T(t);
}
///
@safe unittest
{
struct CustomerRecord
{
string name;
string address;
int customerNum;
}
Nullable!CustomerRecord getByName(string name)
{
//A bunch of hairy stuff
return Nullable!CustomerRecord.init;
}
auto queryResult = getByName("Doe, John");
if (!queryResult.isNull)
{
//Process Mr. Doe's customer record
auto address = queryResult.address;
auto customerNum = queryResult.customerNum;
//Do some things with this customer's info
}
else
{
//Add the customer to the database
}
}
///
@system unittest
{
import std.exception : assertThrown;
auto a = 42.nullable;
assert(!a.isNull);
assert(a.get == 42);
a.nullify();
assert(a.isNull);
assertThrown!Throwable(a.get);
}
@system unittest
{
import std.exception : assertThrown;
Nullable!int a;
assert(a.isNull);
assertThrown!Throwable(a.get);
a = 5;
assert(!a.isNull);
assert(a == 5);
assert(a != 3);
assert(a.get != 3);
a.nullify();
assert(a.isNull);
a = 3;
assert(a == 3);
a *= 6;
assert(a == 18);
a = a;
assert(a == 18);
a.nullify();
assertThrown!Throwable(a += 2);
}
@safe unittest
{
auto k = Nullable!int(74);
assert(k == 74);
k.nullify();
assert(k.isNull);
}
@safe unittest
{
static int f(in Nullable!int x) {
return x.isNull ? 42 : x.get;
}
Nullable!int a;
assert(f(a) == 42);
a = 8;
assert(f(a) == 8);
a.nullify();
assert(f(a) == 42);
}
@system unittest
{
import std.exception : assertThrown;
static struct S { int x; }
Nullable!S s;
assert(s.isNull);
s = S(6);
assert(s == S(6));
assert(s != S(0));
assert(s.get != S(0));
s.x = 9190;
assert(s.x == 9190);
s.nullify();
assertThrown!Throwable(s.x = 9441);
}
@safe unittest
{
// Ensure Nullable can be used in pure/nothrow/@safe environment.
function() @safe pure nothrow
{
Nullable!int n;
assert(n.isNull);
n = 4;
assert(!n.isNull);
assert(n == 4);
n.nullify();
assert(n.isNull);
}();
}
@system unittest
{
// Ensure Nullable can be used when the value is not pure/nothrow/@safe
static struct S
{
int x;
this(this) @system {}
}
Nullable!S s;
assert(s.isNull);
s = S(5);
assert(!s.isNull);
assert(s.x == 5);
s.nullify();
assert(s.isNull);
}
@safe unittest
{
// Bugzilla 9404
alias N = Nullable!int;
void foo(N a)
{
N b;
b = a; // `N b = a;` works fine
}
N n;
foo(n);
}
@safe unittest
{
//Check nullable immutable is constructable
{
auto a1 = Nullable!(immutable int)();
auto a2 = Nullable!(immutable int)(1);
auto i = a2.get;
}
//Check immutable nullable is constructable
{
auto a1 = immutable (Nullable!int)();
auto a2 = immutable (Nullable!int)(1);
auto i = a2.get;
}
}
@safe unittest
{
alias NInt = Nullable!int;
//Construct tests
{
//from other Nullable null
NInt a1;
NInt b1 = a1;
assert(b1.isNull);
//from other Nullable non-null
NInt a2 = NInt(1);
NInt b2 = a2;
assert(b2 == 1);
//Construct from similar nullable
auto a3 = immutable(NInt)();
NInt b3 = a3;
assert(b3.isNull);
}
//Assign tests
{
//from other Nullable null
NInt a1;
NInt b1;
b1 = a1;
assert(b1.isNull);
//from other Nullable non-null
NInt a2 = NInt(1);
NInt b2;
b2 = a2;
assert(b2 == 1);
//Construct from similar nullable
auto a3 = immutable(NInt)();
NInt b3 = a3;
b3 = a3;
assert(b3.isNull);
}
}
@safe unittest
{
//Check nullable is nicelly embedable in a struct
static struct S1
{
Nullable!int ni;
}
static struct S2 //inspired from 9404
{
Nullable!int ni;
this(S2 other)
{
ni = other.ni;
}
void opAssign(S2 other)
{
ni = other.ni;
}
}
foreach (S; AliasSeq!(S1, S2))
{
S a;
S b = a;
S c;
c = a;
}
}
@system unittest
{
// Bugzilla 10268
import std.json;
JSONValue value = null;
auto na = Nullable!JSONValue(value);
struct S1 { int val; }
struct S2 { int* val; }
struct S3 { immutable int* val; }
{
auto sm = S1(1);
immutable si = immutable S1(1);
auto x1 = Nullable!S1(sm);
auto x2 = immutable Nullable!S1(sm);
auto x3 = Nullable!S1(si);
auto x4 = immutable Nullable!S1(si);
assert(x1.val == 1);
assert(x2.val == 1);
assert(x3.val == 1);
assert(x4.val == 1);
}
auto nm = 10;
immutable ni = 10;
{
auto sm = S2(&nm);
immutable si = immutable S2(&ni);
auto x1 = Nullable!S2(sm);
static assert(!__traits(compiles, { auto x2 = immutable Nullable!S2(sm); }));
static assert(!__traits(compiles, { auto x3 = Nullable!S2(si); }));
auto x4 = immutable Nullable!S2(si);
assert(*x1.val == 10);
assert(*x4.val == 10);
}
{
auto sm = S3(&ni);
immutable si = immutable S3(&ni);
auto x1 = Nullable!S3(sm);
auto x2 = immutable Nullable!S3(sm);
auto x3 = Nullable!S3(si);
auto x4 = immutable Nullable!S3(si);
assert(*x1.val == 10);
assert(*x2.val == 10);
assert(*x3.val == 10);
assert(*x4.val == 10);
}
}
@safe unittest
{
// Bugzila 10357
import std.datetime;
Nullable!SysTime time = SysTime(0);
}
@system unittest
{
import std.conv : to;
import std.array;
// Bugzilla 10915
Appender!string buffer;
Nullable!int ni;
assert(ni.to!string() == "Nullable.null");
struct Test { string s; }
alias NullableTest = Nullable!Test;
NullableTest nt = Test("test");
assert(nt.to!string() == `Test("test")`);
NullableTest ntn = Test("null");
assert(ntn.to!string() == `Test("null")`);
class TestToString
{
double d;
this (double d)
{
this.d = d;
}
override string toString()
{
return d.to!string();
}
}
Nullable!TestToString ntts = new TestToString(2.5);
assert(ntts.to!string() == "2.5");
}
/**
Just like $(D Nullable!T), except that the null state is defined as a
particular value. For example, $(D Nullable!(uint, uint.max)) is an
$(D uint) that sets aside the value $(D uint.max) to denote a null
state. $(D Nullable!(T, nullValue)) is more storage-efficient than $(D
Nullable!T) because it does not need to store an extra $(D bool).
Params:
T = The wrapped type for which Nullable provides a null value.
nullValue = The null value which denotes the null state of this
`Nullable`. Must be of type `T`.
*/
struct Nullable(T, T nullValue)
{
private T _value = nullValue;
/**
Constructor initializing $(D this) with $(D value).
Params:
value = The value to initialize this `Nullable` with.
*/
this(T value)
{
_value = value;
}
template toString()
{
import std.format : FormatSpec, formatValue;
// Needs to be a template because of DMD @@BUG@@ 13737.
void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
{
if (isNull)
{
sink.formatValue("Nullable.null", fmt);
}
else
{
sink.formatValue(_value, fmt);
}
}
}
/**
Check if `this` is in the null state.
Returns:
true $(B iff) `this` is in the null state, otherwise false.
*/
@property bool isNull() const
{
//Need to use 'is' if T is a nullable type and
//nullValue is null, or it's a compiler error
static if (is(CommonType!(T, typeof(null)) == T) && nullValue is null)
{
return _value is nullValue;
}
//Need to use 'is' if T is a float type
//because NaN != NaN
else static if (isFloatingPoint!T)
{
return _value is nullValue;
}
else
{
return _value == nullValue;
}
}
///
@system unittest
{
Nullable!(int, -1) ni;
//Initialized to "null" state
assert(ni.isNull);
ni = 0;
assert(!ni.isNull);
}
// https://issues.dlang.org/show_bug.cgi?id=11135
// disable test until https://issues.dlang.org/show_bug.cgi?id=15316 gets fixed
version (none) unittest
{
foreach (T; AliasSeq!(float, double, real))
{
Nullable!(T, T.init) nf;
//Initialized to "null" state
assert(nf.isNull);
assert(nf is typeof(nf).init);
nf = 0;
assert(!nf.isNull);
nf.nullify();
assert(nf.isNull);
}
}
/**
Forces $(D this) to the null state.
*/
void nullify()()
{
_value = nullValue;
}
///
@system unittest
{
Nullable!(int, -1) ni = 0;
assert(!ni.isNull);
ni = -1;
assert(ni.isNull);
}
/**
Assigns $(D value) to the internally-held state. If the assignment
succeeds, $(D this) becomes non-null. No null checks are made. Note
that the assignment may leave $(D this) in the null state.
Params:
value = A value of type `T` to assign to this `Nullable`.
If it is `nullvalue`, then the internal state of
this `Nullable` will be set to null.
*/
void opAssign()(T value)
{
_value = value;
}
/**
If this `Nullable` wraps a type that already has a null value
(such as a pointer), and that null value is not given for
`nullValue`, then assigning the null value to this `Nullable`
is no different than assigning any other value of type `T`,
and the resulting code will look very strange. It is strongly
recommended that this be avoided by using `T`'s "built in"
null value for `nullValue`.
*/
@system unittest
{
//Passes
enum nullVal = cast(int*) 0xCAFEBABE;
Nullable!(int*, nullVal) npi;
assert(npi.isNull);
//Passes?!
npi = null;
assert(!npi.isNull);
}
/**
Gets the value. $(D this) must not be in the null state.
This function is also called for the implicit conversion to $(D T).
Returns:
The value held internally by this `Nullable`.
*/
@property ref inout(T) get() inout
{
//@@@6169@@@: We avoid any call that might evaluate nullValue's %s,
//Because it might messup get's purity and safety inference.
enum message = "Called `get' on null Nullable!(" ~ T.stringof ~ ",nullValue).";
assert(!isNull, message);
return _value;
}
///
@system unittest
{
import std.exception : assertThrown, assertNotThrown;
Nullable!(int, -1) ni;
//`get` is implicitly called. Will throw
//an error in non-release mode
assertThrown!Throwable(ni == 0);
ni = 0;
assertNotThrown!Throwable(ni == 0);
}
/**
Implicitly converts to $(D T).
$(D this) must not be in the null state.
*/
alias get this;
}
/// ditto
auto nullable(alias nullValue, T)(T t)
if (is (typeof(nullValue) == T))
{
return Nullable!(T, nullValue)(t);
}
///
@safe unittest
{
Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle)
{
//Find the needle, returning -1 if not found
return Nullable!(size_t, size_t.max).init;
}
void sendLunchInvite(string name)
{
}
//It's safer than C...
auto coworkers = ["Jane", "Jim", "Marry", "Fred"];
auto pos = indexOf(coworkers, "Bob");
if (!pos.isNull)
{
//Send Bob an invitation to lunch
sendLunchInvite(coworkers[pos]);
}
else
{
//Bob not found; report the error
}
//And there's no overhead
static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof);
}
///
@system unittest
{
import std.exception : assertThrown;
Nullable!(int, int.min) a;
assert(a.isNull);
assertThrown!Throwable(a.get);
a = 5;
assert(!a.isNull);
assert(a == 5);
static assert(a.sizeof == int.sizeof);
}
///
@safe unittest
{
auto a = nullable!(int.min)(8);
assert(a == 8);
a.nullify();
assert(a.isNull);
}
@safe unittest
{
static int f(in Nullable!(int, int.min) x) {
return x.isNull ? 42 : x.get;
}
Nullable!(int, int.min) a;
assert(f(a) == 42);
a = 8;
assert(f(a) == 8);
a.nullify();
assert(f(a) == 42);
}
@safe unittest
{
// Ensure Nullable can be used in pure/nothrow/@safe environment.
function() @safe pure nothrow
{
Nullable!(int, int.min) n;
assert(n.isNull);
n = 4;
assert(!n.isNull);
assert(n == 4);
n.nullify();
assert(n.isNull);
}();
}
@system unittest
{
// Ensure Nullable can be used when the value is not pure/nothrow/@system
static struct S
{
int x;
bool opEquals(const S s) const @system { return s.x == x; }
}
Nullable!(S, S(711)) s;
assert(s.isNull);
s = S(5);
assert(!s.isNull);
assert(s.x == 5);
s.nullify();
assert(s.isNull);
}
@safe unittest
{
//Check nullable is nicelly embedable in a struct
static struct S1
{
Nullable!(int, 0) ni;
}
static struct S2 //inspired from 9404
{
Nullable!(int, 0) ni;
this(S2 other)
{
ni = other.ni;
}
void opAssign(S2 other)
{
ni = other.ni;
}
}
foreach (S; AliasSeq!(S1, S2))
{
S a;
S b = a;
S c;
c = a;
}
}
@system unittest
{
import std.conv : to;
// Bugzilla 10915
Nullable!(int, 1) ni = 1;
assert(ni.to!string() == "Nullable.null");
struct Test { string s; }
alias NullableTest = Nullable!(Test, Test("null"));
NullableTest nt = Test("test");
assert(nt.to!string() == `Test("test")`);
NullableTest ntn = Test("null");
assert(ntn.to!string() == "Nullable.null");
class TestToString
{
double d;
this(double d)
{
this.d = d;
}
override string toString()
{
return d.to!string();
}
}
alias NullableTestToString = Nullable!(TestToString, null);
NullableTestToString ntts = new TestToString(2.5);
assert(ntts.to!string() == "2.5");
}
/**
Just like $(D Nullable!T), except that the object refers to a value
sitting elsewhere in memory. This makes assignments overwrite the
initially assigned value. Internally $(D NullableRef!T) only stores a
pointer to $(D T) (i.e., $(D Nullable!T.sizeof == (T*).sizeof)).
*/
struct NullableRef(T)
{
private T* _value;
/**
Constructor binding $(D this) to $(D value).
Params:
value = The value to bind to.
*/
this(T* value) @safe pure nothrow
{
_value = value;
}
template toString()
{
import std.format : FormatSpec, formatValue;
// Needs to be a template because of DMD @@BUG@@ 13737.
void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
{
if (isNull)
{
sink.formatValue("Nullable.null", fmt);
}
else
{
sink.formatValue(*_value, fmt);
}
}
}
/**
Binds the internal state to $(D value).
Params:
value = A pointer to a value of type `T` to bind this `NullableRef` to.
*/
void bind(T* value) @safe pure nothrow
{
_value = value;
}
///
@safe unittest
{
NullableRef!int nr = new int(42);
assert(nr == 42);
int* n = new int(1);
nr.bind(n);
assert(nr == 1);
}
/**
Returns $(D true) if and only if $(D this) is in the null state.
Returns:
true if `this` is in the null state, otherwise false.
*/
@property bool isNull() const @safe pure nothrow
{
return _value is null;
}
///
@safe unittest
{
NullableRef!int nr;
assert(nr.isNull);
int* n = new int(42);
nr.bind(n);
assert(!nr.isNull && nr == 42);
}
/**
Forces $(D this) to the null state.
*/
void nullify() @safe pure nothrow
{
_value = null;
}
///
@safe unittest
{
NullableRef!int nr = new int(42);
assert(!nr.isNull);
nr.nullify();
assert(nr.isNull);
}
/**
Assigns $(D value) to the internally-held state.
Params:
value = A value of type `T` to assign to this `NullableRef`.
If the internal state of this `NullableRef` has not
been initialized, an error will be thrown in
non-release mode.
*/
void opAssign()(T value)
if (isAssignable!T) //@@@9416@@@
{
enum message = "Called `opAssign' on null NullableRef!" ~ T.stringof ~ ".";
assert(!isNull, message);
*_value = value;
}
///
@system unittest
{
import std.exception : assertThrown, assertNotThrown;
NullableRef!int nr;
assert(nr.isNull);
assertThrown!Throwable(nr = 42);
nr.bind(new int(0));
assert(!nr.isNull);
assertNotThrown!Throwable(nr = 42);
assert(nr == 42);
}
/**
Gets the value. $(D this) must not be in the null state.
This function is also called for the implicit conversion to $(D T).
*/
@property ref inout(T) get() inout @safe pure nothrow
{
enum message = "Called `get' on null NullableRef!" ~ T.stringof ~ ".";
assert(!isNull, message);
return *_value;
}
///
@system unittest
{
import std.exception : assertThrown, assertNotThrown;
NullableRef!int nr;
//`get` is implicitly called. Will throw
//an error in non-release mode
assertThrown!Throwable(nr == 0);
nr.bind(new int(0));
assertNotThrown!Throwable(nr == 0);
}
/**
Implicitly converts to $(D T).
$(D this) must not be in the null state.
*/
alias get this;
}
/// ditto
auto nullableRef(T)(T* t)
{
return NullableRef!T(t);
}
///
@system unittest
{
import std.exception : assertThrown;
int x = 5, y = 7;
auto a = nullableRef(&x);
assert(!a.isNull);
assert(a == 5);
assert(x == 5);
a = 42;
assert(x == 42);
assert(!a.isNull);
assert(a == 42);
a.nullify();
assert(x == 42);
assert(a.isNull);
assertThrown!Throwable(a.get);
assertThrown!Throwable(a = 71);
a.bind(&y);
assert(a == 7);
y = 135;
assert(a == 135);
}
@system unittest
{
static int f(in NullableRef!int x) {
return x.isNull ? 42 : x.get;
}
int x = 5;
auto a = nullableRef(&x);
assert(f(a) == 5);
a.nullify();
assert(f(a) == 42);
}
@safe unittest
{
// Ensure NullableRef can be used in pure/nothrow/@safe environment.
function() @safe pure nothrow
{
auto storage = new int;
*storage = 19902;
NullableRef!int n;
assert(n.isNull);
n.bind(storage);
assert(!n.isNull);
assert(n == 19902);
n = 2294;
assert(n == 2294);
assert(*storage == 2294);
n.nullify();
assert(n.isNull);
}();
}
@system unittest
{
// Ensure NullableRef can be used when the value is not pure/nothrow/@safe
static struct S
{
int x;
this(this) @system {}
bool opEquals(const S s) const @system { return s.x == x; }
}
auto storage = S(5);
NullableRef!S s;
assert(s.isNull);
s.bind(&storage);
assert(!s.isNull);
assert(s.x == 5);
s.nullify();
assert(s.isNull);
}
@safe unittest
{
//Check nullable is nicelly embedable in a struct
static struct S1
{
NullableRef!int ni;
}
static struct S2 //inspired from 9404
{
NullableRef!int ni;
this(S2 other)
{
ni = other.ni;
}
void opAssign(S2 other)
{
ni = other.ni;
}
}
foreach (S; AliasSeq!(S1, S2))
{
S a;
S b = a;
S c;
c = a;
}
}
@system unittest
{
import std.conv : to;
// Bugzilla 10915
NullableRef!int nri;
assert(nri.to!string() == "Nullable.null");
struct Test
{
string s;
}
NullableRef!Test nt = new Test("test");
assert(nt.to!string() == `Test("test")`);
class TestToString
{
double d;
this(double d)
{
this.d = d;
}
override string toString()
{
return d.to!string();
}
}
TestToString tts = new TestToString(2.5);
NullableRef!TestToString ntts = &tts;
assert(ntts.to!string() == "2.5");
}
/**
$(D BlackHole!Base) is a subclass of $(D Base) which automatically implements
all abstract member functions in $(D Base) as do-nothing functions. Each
auto-implemented function just returns the default value of the return type
without doing anything.
The name came from
$(HTTP search.cpan.org/~sburke/Class-_BlackHole-0.04/lib/Class/_BlackHole.pm, Class::_BlackHole)
Perl module by Sean M. Burke.
Params:
Base = A non-final class for `BlackHole` to inherit from.
See_Also:
$(LREF AutoImplement), $(LREF generateEmptyFunction)
*/
alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFunction);
///
@system unittest
{
import std.math : isNaN;
static abstract class C
{
int m_value;
this(int v) { m_value = v; }
int value() @property { return m_value; }
abstract real realValue() @property;
abstract void doSomething();
}
auto c = new BlackHole!C(42);
assert(c.value == 42);
// Returns real.init which is NaN
assert(c.realValue.isNaN);
// Abstract functions are implemented as do-nothing
c.doSomething();
}
@system unittest
{
import std.math : isNaN;
// return default
{
interface I_1 { real test(); }
auto o = new BlackHole!I_1;
assert(o.test().isNaN()); // NaN
}
// doc example
{
static class C
{
int m_value;
this(int v) { m_value = v; }
int value() @property { return m_value; }
abstract real realValue() @property;
abstract void doSomething();
}
auto c = new BlackHole!C(42);
assert(c.value == 42);
assert(c.realValue.isNaN); // NaN
c.doSomething();
}
// Bugzilla 12058
interface Foo
{
inout(Object) foo() inout;
}
BlackHole!Foo o;
}
/**
$(D WhiteHole!Base) is a subclass of $(D Base) which automatically implements
all abstract member functions as functions that always fail. These functions
simply throw an $(D Error) and never return. `Whitehole` is useful for
trapping the use of class member functions that haven't been implemented.
The name came from
$(HTTP search.cpan.org/~mschwern/Class-_WhiteHole-0.04/lib/Class/_WhiteHole.pm, Class::_WhiteHole)
Perl module by Michael G Schwern.
Params:
Base = A non-final class for `WhiteHole` to inherit from.
See_Also:
$(LREF AutoImplement), $(LREF generateAssertTrap)
*/
alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap, isAbstractFunction);
///
@system unittest
{
import std.exception : assertThrown;
static class C
{
abstract void notYetImplemented();
}
auto c = new WhiteHole!C;
assertThrown!NotImplementedError(c.notYetImplemented()); // throws an Error
}
// / ditto
class NotImplementedError : Error
{
this(string method)
{
super(method ~ " is not implemented");
}
}
@system unittest
{
import std.exception : assertThrown;
// nothrow
{
interface I_1
{
void foo();
void bar() nothrow;
}
auto o = new WhiteHole!I_1;
assertThrown!NotImplementedError(o.foo());
assertThrown!NotImplementedError(o.bar());
}
// doc example
{
static class C
{
abstract void notYetImplemented();
}
auto c = new WhiteHole!C;
try
{
c.notYetImplemented();
assert(0);
}
catch (Error e) {}
}
}
/**
$(D AutoImplement) automatically implements (by default) all abstract member
functions in the class or interface $(D Base) in specified way.
Params:
how = template which specifies _how functions will be implemented/overridden.
Two arguments are passed to $(D how): the type $(D Base) and an alias
to an implemented function. Then $(D how) must return an implemented
function body as a string.
The generated function body can use these keywords:
$(UL
$(LI $(D a0), $(D a1), &hellip;: arguments passed to the function;)
$(LI $(D args): a tuple of the arguments;)
$(LI $(D self): an alias to the function itself;)
$(LI $(D parent): an alias to the overridden function (if any).)
)
You may want to use templated property functions (instead of Implicit
Template Properties) to generate complex functions:
--------------------
// Prints log messages for each call to overridden functions.
string generateLogger(C, alias fun)() @property
{
import std.traits;
enum qname = C.stringof ~ "." ~ __traits(identifier, fun);
string stmt;
stmt ~= q{ struct Importer { import std.stdio; } };
stmt ~= `Importer.writeln("Log: ` ~ qname ~ `(", args, ")");`;
static if (!__traits(isAbstractFunction, fun))
{
static if (is(ReturnType!fun == void))
stmt ~= q{ parent(args); };
else
stmt ~= q{
auto r = parent(args);
Importer.writeln("--> ", r);
return r;
};
}
return stmt;
}
--------------------
what = template which determines _what functions should be
implemented/overridden.
An argument is passed to $(D what): an alias to a non-final member
function in $(D Base). Then $(D what) must return a boolean value.
Return $(D true) to indicate that the passed function should be
implemented/overridden.
--------------------
// Sees if fun returns something.
enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void);
--------------------
Note:
Generated code is inserted in the scope of $(D std.typecons) module. Thus,
any useful functions outside $(D std.typecons) cannot be used in the generated
code. To workaround this problem, you may $(D import) necessary things in a
local struct, as done in the $(D generateLogger()) template in the above
example.
BUGS:
$(UL
$(LI Variadic arguments to constructors are not forwarded to super.)
$(LI Deep interface inheritance causes compile error with messages like
"Error: function std.typecons._AutoImplement!(Foo)._AutoImplement.bar
does not override any function". [$(BUGZILLA 2525), $(BUGZILLA 3525)] )
$(LI The $(D parent) keyword is actually a delegate to the super class'
corresponding member function. [$(BUGZILLA 2540)] )
$(LI Using alias template parameter in $(D how) and/or $(D what) may cause
strange compile error. Use template tuple parameter instead to workaround
this problem. [$(BUGZILLA 4217)] )
)
*/
class AutoImplement(Base, alias how, alias what = isAbstractFunction) : Base
{
private alias autoImplement_helper_ =
AutoImplement_Helper!("autoImplement_helper_", "Base", Base, how, what);
mixin(autoImplement_helper_.code);