phobos/std/typecons.d
2014-12-18 16:39:18 +02:00

5888 lines
155 KiB
D

// 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)
Macros:
WIKI = Phobos/StdVariant
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: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
Authors: $(WEB erdani.org, Andrei Alexandrescu),
$(WEB bartoszmilewski.wordpress.com, Bartosz Milewski),
Don Clugston,
Shin Fujishiro,
Kenji Hara
*/
module std.typecons;
import std.traits;
// FIXME
import std.typetuple; // : TypeTuple, allSatisfy;
debug(Unique) import std.stdio;
/**
Encapsulates unique ownership of a resource. Resource of type $(D T) is
deleted at the end of the scope, unless it is transferred. The
transfer can be explicit, by calling $(D release), or implicit, when
returning Unique from a function. The resource can be a polymorphic
class object, in which case Unique behaves polymorphically too.
*/
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) delete _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. */
Unique release()
{
debug(Unique) writeln("Release");
auto u = Unique(_p);
assert(_p is null);
debug(Unique) writeln("return from Release");
return u;
}
/** Forwards member access to contents. */
RefT opDot() { return _p; }
/**
Postblit operator is undefined to prevent the cloning of $(D Unique) objects.
*/
@disable this(this);
private:
RefT _p;
}
///
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);
}
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);
}
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);
}
unittest
{
debug(Unique) writeln("Unique struct");
struct Foo
{
~this() { debug(Unique) writeln(" Foo destructor"); }
int val() const { return 3; }
}
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);
}
/**
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. type tuple iteration), all of which use
zero-based indexing.
Example:
----
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.
Example:
----
alias Entry = Tuple!(int, "index", string, "value");
Entry e;
e.index = 4;
e.value = "Hello";
assert(e[1] == "Hello");
assert(e[0] == 4);
----
Tuples with named fields are distinct types from tuples with unnamed
fields, i.e. each naming imparts a separate type for the tuple. Two
tuple differing in naming only are still distinct, even though they
might have the same structure.
Example:
----
Tuple!(int, "x", int, "y") point1;
Tuple!(int, int) point2;
assert(!is(typeof(point1) == typeof(point2))); // passes
----
*/
template Tuple(Specs...)
{
import std.typetuple : 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 = TypeTuple!();
}
else static if (is(Specs[0]))
{
static if (is(typeof(Specs[1]) : string))
{
alias parseSpecs =
TypeTuple!(FieldSpec!(Specs[0 .. 2]),
parseSpecs!(Specs[2 .. $]));
}
else
{
alias parseSpecs =
TypeTuple!(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 = TypeTuple!(spec.Type);
}
else
{
alias expandSpec = TypeTuple!(spec.Type, spec.name);
}
}
enum areCompatibleTuples(Tup1, Tup2, string op) = isTuple!Tup2 && is(typeof(
{
Tup1 tup1 = void;
Tup2 tup2 = void;
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 type of the tuple's components.
*/
alias Types = staticMap!(extractType, fieldSpecs);
/**
* The names of the tuple's components. Unnamed fields have empty names.
*
* Examples:
* ----
* alias Fields = Tuple!(int, "id", string, float);
* static assert(Fields.fieldNames == TypeTuple!("id", "", ""));
* ----
*/
alias fieldNames = staticMap!(extractName, fieldSpecs);
/**
* 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 components
* were listed as a list of values. (Ordinarily, a $(D Tuple) acts as a
* single value.)
*
* Examples:
* ----
* auto t = tuple(1, " hello ", 2.3);
* writeln(t); // Tuple!(int, string, double)(1, " hello ", 2.3)
* writeln(t.expand); // 1 hello 2.3
* ----
*/
Types expand;
mixin(injectNamedFields());
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.
*/
static if (Types.length > 0)
{
this(Types values)
{
field[] = values[];
}
}
/**
* Constructor taking a compatible array.
*
* Examples:
* ----
* int[2] ints;
* Tuple!(int, int) t = ints;
* ----
*/
this(U, size_t n)(U[n] values)
if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types))
{
foreach (i, _; Types)
{
field[i] = values[i];
}
}
/**
* Constructor taking a compatible tuple.
*/
this(U)(U another)
if (areBuildCompatibleTuples!(typeof(this), U))
{
field[] = another.field[];
}
/**
* Comparison for equality.
*/
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[];
}
/**
* Comparison for ordering.
*/
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;
}
/**
* Assignment from another tuple. Each element of the source must be
* implicitly assignable to the respective element of the target.
*/
void opAssign(R)(auto ref R rhs)
if (areCompatibleTuples!(typeof(this), R, "="))
{
import std.algorithm : 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[];
}
}
/**
* Takes a slice of the tuple.
*
* Examples:
* ----
* 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);
* ----
*/
@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]);
}
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;
}
/**
* Converts to string.
*/
void toString()(scope void delegate(const(char)[]) sink)
{
enum header = typeof(this).stringof ~ "(",
footer = ")",
separator = ", ";
sink(header);
foreach (i, Type; Types)
{
static if (i > 0)
{
sink(separator);
}
// TODO: Change this once toString() works for shared objects.
static if (is(Type == class) && is(typeof(Type.init) == shared))
{
sink(Type.stringof);
}
else
{
import std.format : FormatSpec, formatElement;
FormatSpec!char f;
formatElement(sink, field[i], f);
}
}
sink(footer);
}
string toString()()
{
import std.conv : to;
return this.to!string;
}
}
}
/**
Return a copy of a Tuple with its fields in reverse order.
*/
ReverseTupleType!T reverse(T)(T t)
if (isTuple!T)
{
import std.typetuple : 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));
typeof(return) result;
auto tup = t.expand;
result.expand = Reverse!tup;
return result;
}
///
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 = TypeTuple!(T[$-2], T[$-1], ReverseTupleSpecs!(T[0 .. $-2]));
}
else
{
alias ReverseTupleSpecs = TypeTuple!(T[$-1], ReverseTupleSpecs!(T[0 .. $-1]));
}
}
else
{
alias ReverseTupleSpecs = T;
}
}
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 _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");
}
}
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)));
}
{
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)));
}
unittest
{
// Bugzilla 10686
immutable Tuple!(int) t1;
auto r1 = t1[0]; // OK
immutable Tuple!(int, "x") t2;
auto r2 = t2[0]; // error
}
unittest
{
import std.exception : assertCTFEable;
// Bugzilla 10218
assertCTFEable!(
{
auto t = tuple(1);
t = tuple(2); // assignment
});
}
unittest
{
class Foo{}
Tuple!(immutable(Foo)[]) a;
}
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
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 == TypeTuple!("id", "", ""));
}
// Bugzilla 13837
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)));
}
unittest
{
class C {}
Tuple!(Rebindable!(const C)) a;
Tuple!(const C) b;
a = b;
}
/**
Returns a $(D Tuple) object instantiated and initialized according to
the arguments.
Example:
----
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");
----
*/
template tuple(Names...)
{
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 TypeTuple!(A[0], B[0]) and;
}
template and(B...) if (B.length != 1)
{
alias TypeTuple!(A[0], B[0],
Interleave!(A[1..$]).and!(B[1..$])) and;
}
}
return Tuple!(Interleave!(Args).and!(Names))(args);
}
}
}
/**
Returns $(D true) if and only if $(D T) is an instance of the
$(D Tuple) struct template.
*/
template isTuple(T)
{
static if (is(Unqual!T Unused : Tuple!Specs, Specs...))
{
enum isTuple = true;
}
else
{
enum isTuple = false;
}
}
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)));
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))
{
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 ref 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. However,
$(D Rebindable!(T)) does not compile if $(D T) is a non-class type.
Regular $(D const) object references cannot be reassigned:
----
class Widget { int x; int y() const { return x; } }
const a = new Widget;
a.y(); // fine
a.x = 5; // error! can't modify const a
a = new Widget; // error! can't modify const a
----
However, $(D Rebindable!(Widget)) does allow reassignment, while
otherwise behaving exactly like a $(D const Widget):
----
auto a = Rebindable!(const Widget)(new Widget);
a.y(); // fine
a.x = 5; // error! can't modify const a
a = new Widget; // fine
----
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).
*/
template Rebindable(T) if (is(T == class) || is(T == interface) || isDynamicArray!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;
}
}
/**
Convenience function for creating a $(D Rebindable) using automatic type
inference.
*/
Rebindable!T rebindable(T)(T obj)
if (is(T == class) || is(T == interface) || isDynamicArray!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).
*/
Rebindable!T rebindable(T)(Rebindable!T obj)
{
return obj;
}
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; TypeTuple!(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])));
}
/**
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)
*/
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;
}
}
///
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);
}
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.
Returns a declaration to be mixed in.
Example:
---
struct Banner {
mixin(alignForSize!(byte[6], double)(["name", "height"]));
}
---
Alignment is not always optimal for 80-bit reals, nor for structs declared
as align(1).
*/
string alignForSize(E...)(string[] 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;
}
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);
}
/**
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.
Example:
----
Nullable!int a;
assert(a.isNull);
a = 5;
assert(!a.isNull);
assert(a == 5);
----
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).
*/
this(inout T value) inout
{
_value = value;
_isNull = false;
}
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);
}
}
}
/**
Returns $(D true) if and only if $(D this) is in the null state.
*/
@property bool isNull() const @safe pure nothrow
{
return _isNull;
}
/**
Forces $(D this) to the null state.
*/
void nullify()()
{
.destroy(_value);
_isNull = true;
}
/**
Assigns $(D value) to the internally-held state. If the assignment
succeeds, $(D this) becomes non-null.
*/
void opAssign()(T value)
{
_value = value;
_isNull = false;
}
/**
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 Nullable!" ~ T.stringof ~ ".";
assert(!isNull, message);
return _value;
}
/**
Implicitly converts to $(D T).
$(D this) must not be in the null state.
*/
alias get this;
}
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);
}
unittest
{
auto k = Nullable!int(74);
assert(k == 74);
k.nullify();
assert(k.isNull);
}
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);
}
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);
}
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);
}();
}
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);
}
unittest
{
// Bugzilla 9404
alias N = Nullable!int;
void foo(N a)
{
N b;
b = a; // `N b = a;` works fine
}
N n;
foo(n);
}
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;
}
}
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);
}
}
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; TypeTuple!(S1, S2))
{
S a;
S b = a;
S c;
c = a;
}
}
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);
static assert( __traits(compiles, { auto x1 = Nullable!S1(sm); }));
static assert( __traits(compiles, { auto x2 = immutable Nullable!S1(sm); }));
static assert( __traits(compiles, { auto x3 = Nullable!S1(si); }));
static assert( __traits(compiles, { auto x4 = immutable Nullable!S1(si); }));
}
auto nm = 10;
immutable ni = 10;
{
auto sm = S2(&nm);
immutable si = immutable S2(&ni);
static assert( __traits(compiles, { auto x = Nullable!S2(sm); }));
static assert(!__traits(compiles, { auto x = immutable Nullable!S2(sm); }));
static assert(!__traits(compiles, { auto x = Nullable!S2(si); }));
static assert( __traits(compiles, { auto x = immutable Nullable!S2(si); }));
}
{
auto sm = S3(&ni);
immutable si = immutable S3(&ni);
static assert( __traits(compiles, { auto x = Nullable!S3(sm); }));
static assert( __traits(compiles, { auto x = immutable Nullable!S3(sm); }));
static assert( __traits(compiles, { auto x = Nullable!S3(si); }));
static assert( __traits(compiles, { auto x = immutable Nullable!S3(si); }));
}
}
unittest
{
// Bugzila 10357
import std.datetime;
Nullable!SysTime time = SysTime(0);
}
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).
*/
struct Nullable(T, T nullValue)
{
private T _value = nullValue;
/**
Constructor initializing $(D this) with $(D value).
*/
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);
}
}
}
/**
Returns $(D true) if and only if $(D this) is in the null state.
*/
@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;
}
else
{
return _value == nullValue;
}
}
/**
Forces $(D this) to the null state.
*/
void nullify()()
{
_value = nullValue;
}
/**
Assigns $(D value) to the internally-held state. No null checks are
made. Note that the assignment may leave $(D this) in the null state.
*/
void opAssign()(T value)
{
_value = value;
}
/**
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
{
//@@@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;
}
/**
Implicitly converts to $(D T).
Gets the value. $(D this) must not be in the null state.
*/
alias get this;
}
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);
}
unittest
{
auto a = Nullable!(int, int.min)(8);
assert(a == 8);
a.nullify();
assert(a.isNull);
}
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);
}
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);
}();
}
unittest
{
// Ensure Nullable can be used when the value is not pure/nothrow/@safe
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);
}
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; TypeTuple!(S1, S2))
{
S a;
S b = a;
S c;
c = a;
}
}
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) with $(D value).
*/
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).
*/
void bind(T* value) @safe pure nothrow
{
_value = value;
}
/**
Returns $(D true) if and only if $(D this) is in the null state.
*/
@property bool isNull() const @safe pure nothrow
{
return _value is null;
}
/**
Forces $(D this) to the null state.
*/
void nullify() @safe pure nothrow
{
_value = null;
}
/**
Assigns $(D value) to the internally-held state.
*/
void opAssign()(T value)
if (isAssignable!T) //@@@9416@@@
{
enum message = "Called `opAssign' on null NullableRef!" ~ T.stringof ~ ".";
assert(!isNull, message);
*_value = value;
}
/**
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;
}
/**
Implicitly converts to $(D T).
$(D this) must not be in the null state.
*/
alias get this;
}
unittest
{
import std.exception : assertThrown;
int x = 5, y = 7;
auto a = NullableRef!(int)(&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);
}
unittest
{
static int f(in NullableRef!int x) {
return x.isNull ? 42 : x.get;
}
int x = 5;
auto a = NullableRef!int(&x);
assert(f(a) == 5);
a.nullify();
assert(f(a) == 42);
}
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);
}();
}
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);
}
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; TypeTuple!(S1, S2))
{
S a;
S b = a;
S c;
c = a;
}
}
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
$(WEB search.cpan.org/~sburke/Class-_BlackHole-0.04/lib/Class/_BlackHole.pm, Class::_BlackHole)
Perl module by Sean M. Burke.
Example:
--------------------
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();
}
void main()
{
auto c = new BlackHole!C(42);
writeln(c.value); // prints "42"
// Abstract functions are implemented as do-nothing:
writeln(c.realValue); // prints "NaN"
c.doSomething(); // does nothing
}
--------------------
See_Also:
AutoImplement, generateEmptyFunction
*/
alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFunction);
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;
// Bugzilla 12464
import std.stream;
import std.typecons;
BlackHole!OutputStream dout;
}
/**
$(D WhiteHole!Base) is a subclass of $(D Base) which automatically implements
all abstract member functions as throw-always functions. Each auto-implemented
function fails with throwing an $(D Error) and does never return. Useful for
trapping use of not-yet-implemented functions.
The name came from
$(WEB search.cpan.org/~mschwern/Class-_WhiteHole-0.04/lib/Class/_WhiteHole.pm, Class::_WhiteHole)
Perl module by Michael G Schwern.
Example:
--------------------
class C
{
abstract void notYetImplemented();
}
void main()
{
auto c = new WhiteHole!C;
c.notYetImplemented(); // throws an Error
}
--------------------
See_Also:
AutoImplement, generateAssertTrap
*/
alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap, isAbstractFunction);
// / ditto
class NotImplementedError : Error
{
this(string method)
{
super(method ~ " is not implemented");
}
}
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);
}
/*
* Code-generating stuffs are encupsulated in this helper template so that
* namespace pollution, which can cause name confliction with Base's public
* members, should be minimized.
*/
private template AutoImplement_Helper(string myName, string baseName,
Base, alias generateMethodBody, alias cherrypickMethod)
{
private static:
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Internal stuffs
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Returns function overload sets in the class C, filtered with pred.
template enumerateOverloads(C, alias pred)
{
template Impl(names...)
{
import std.typetuple : Filter;
static if (names.length > 0)
{
alias methods = Filter!(pred, MemberFunctionsTuple!(C, names[0]));
alias next = Impl!(names[1 .. $]);
static if (methods.length > 0)
alias Impl = TypeTuple!(OverloadSet!(names[0], methods), next);
else
alias Impl = next;
}
else
alias Impl = TypeTuple!();
}
alias enumerateOverloads = Impl!(__traits(allMembers, C));
}
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Target functions
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Add a non-final check to the cherrypickMethod.
enum bool canonicalPicker(fun.../+[BUG 4217]+/) =
!__traits(isFinalFunction, fun[0]) && cherrypickMethod!(fun);
/*
* A tuple of overload sets, each item of which consists of functions to be
* implemented by the generated code.
*/
alias targetOverloadSets = enumerateOverloads!(Base, canonicalPicker);
/*
* A tuple of the super class' constructors. Used for forwarding
* constructor calls.
*/
static if (__traits(hasMember, Base, "__ctor"))
alias ctorOverloadSet = OverloadSet!("__ctor", __traits(getOverloads, Base, "__ctor"));
else
alias ctorOverloadSet = OverloadSet!("__ctor"); // empty
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Type information
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
/*
* The generated code will be mixed into AutoImplement, which will be
* instantiated in this module's scope. Thus, any user-defined types are
* out of scope and cannot be used directly (i.e. by their names).
*
* We will use FuncInfo instances for accessing return types and parameter
* types of the implemented functions. The instances will be populated to
* the AutoImplement's scope in a certain way; see the populate() below.
*/
// Returns the preferred identifier for the FuncInfo instance for the i-th
// overloaded function with the name.
template INTERNAL_FUNCINFO_ID(string name, size_t i)
{
import std.format : format;
enum string INTERNAL_FUNCINFO_ID = format("F_%s_%s", name, i);
}
/*
* Insert FuncInfo instances about all the target functions here. This
* enables the generated code to access type information via, for example,
* "autoImplement_helper_.F_foo_1".
*/
template populate(overloads...)
{
static if (overloads.length > 0)
{
mixin populate!(overloads[0].name, overloads[0].contents);
mixin populate!(overloads[1 .. $]);
}
}
template populate(string name, methods...)
{
static if (methods.length > 0)
{
mixin populate!(name, methods[0 .. $ - 1]);
//
alias target = methods[$ - 1];
enum ith = methods.length - 1;
mixin("alias " ~ INTERNAL_FUNCINFO_ID!(name, ith) ~ " = FuncInfo!target;");
}
}
public mixin populate!(targetOverloadSets);
public mixin populate!( ctorOverloadSet );
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Code-generating policies
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
/* Common policy configurations for generating constructors and methods. */
template CommonGeneratingPolicy()
{
// base class identifier which generated code should use
enum string BASE_CLASS_ID = baseName;
// FuncInfo instance identifier which generated code should use
template FUNCINFO_ID(string name, size_t i)
{
enum string FUNCINFO_ID =
myName ~ "." ~ INTERNAL_FUNCINFO_ID!(name, i);
}
}
/* Policy configurations for generating constructors. */
template ConstructorGeneratingPolicy()
{
mixin CommonGeneratingPolicy;
/* Generates constructor body. Just forward to the base class' one. */
string generateFunctionBody(ctor.../+[BUG 4217]+/)() @property
{
enum varstyle = variadicFunctionStyle!(typeof(&ctor[0]));
static if (varstyle & (Variadic.c | Variadic.d))
{
// the argptr-forwarding problem
//pragma(msg, "Warning: AutoImplement!(", Base, ") ",
// "ignored variadic arguments to the constructor ",
// FunctionTypeOf!(typeof(&ctor[0])) );
}
return "super(args);";
}
}
/* Policy configurations for genearting target methods. */
template MethodGeneratingPolicy()
{
mixin CommonGeneratingPolicy;
/* Geneartes method body. */
string generateFunctionBody(func.../+[BUG 4217]+/)() @property
{
return generateMethodBody!(Base, func); // given
}
}
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Generated code
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
alias ConstructorGenerator = MemberFunctionGenerator!(ConstructorGeneratingPolicy!());
alias MethodGenerator = MemberFunctionGenerator!(MethodGeneratingPolicy!());
public enum string code =
ConstructorGenerator.generateCode!( ctorOverloadSet ) ~ "\n" ~
MethodGenerator.generateCode!(targetOverloadSets);
debug (SHOW_GENERATED_CODE)
{
pragma(msg, "-------------------- < ", Base, " >");
pragma(msg, code);
pragma(msg, "--------------------");
}
}
//debug = SHOW_GENERATED_CODE;
unittest
{
import core.vararg;
// no function to implement
{
interface I_1 {}
auto o = new BlackHole!I_1;
}
// parameters
{
interface I_3 { void test(int, in int, out int, ref int, lazy int); }
auto o = new BlackHole!I_3;
}
// use of user-defined type
{
struct S {}
interface I_4 { S test(); }
auto o = new BlackHole!I_4;
}
// overloads
{
interface I_5
{
void test(string);
real test(real);
int test();
}
auto o = new BlackHole!I_5;
}
// constructor forwarding
{
static class C_6
{
this(int n) { assert(n == 42); }
this(string s) { assert(s == "Deeee"); }
this(...) {}
}
auto o1 = new BlackHole!C_6(42);
auto o2 = new BlackHole!C_6("Deeee");
auto o3 = new BlackHole!C_6(1, 2, 3, 4);
}
// attributes
{
interface I_7
{
ref int test_ref();
int test_pure() pure;
int test_nothrow() nothrow;
int test_property() @property;
int test_safe() @safe;
int test_trusted() @trusted;
int test_system() @system;
int test_pure_nothrow() pure nothrow;
}
auto o = new BlackHole!I_7;
}
// storage classes
{
interface I_8
{
void test_const() const;
void test_immutable() immutable;
void test_shared() shared;
void test_shared_const() shared const;
}
auto o = new BlackHole!I_8;
}
/+ // deep inheritance
{
// XXX [BUG 2525,3525]
// NOTE: [r494] func.c(504-571) FuncDeclaration::semantic()
interface I { void foo(); }
interface J : I {}
interface K : J {}
static abstract class C_9 : K {}
auto o = new BlackHole!C_9;
}+/
}
version(unittest)
{
// Issue 10647
private string generateDoNothing(C, alias fun)() @property
{
string stmt;
static if (is(ReturnType!fun == void))
stmt ~= "";
else
{
string returnType = ReturnType!fun.stringof;
stmt ~= "return "~returnType~".init;";
}
return stmt;
}
private template isAlwaysTrue(alias fun)
{
enum isAlwaysTrue = true;
}
// Do nothing template
private template DoNothing(Base)
{
alias DoNothing = AutoImplement!(Base, generateDoNothing, isAlwaysTrue);
}
// A class to be overridden
private class Foo{
void bar(int a) { }
}
}
unittest
{
auto foo = new DoNothing!Foo();
foo.bar(13);
}
/*
Used by MemberFunctionGenerator.
*/
package template OverloadSet(string nam, T...)
{
enum string name = nam;
alias contents = T;
}
/*
Used by MemberFunctionGenerator.
*/
package template FuncInfo(alias func, /+[BUG 4217 ?]+/ T = typeof(&func))
{
alias RT = ReturnType!T;
alias PT = ParameterTypeTuple!T;
}
package template FuncInfo(Func)
{
alias RT = ReturnType!Func;
alias PT = ParameterTypeTuple!Func;
}
/*
General-purpose member function generator.
--------------------
template GeneratingPolicy()
{
// [optional] the name of the class where functions are derived
enum string BASE_CLASS_ID;
// [optional] define this if you have only function types
enum bool WITHOUT_SYMBOL;
// [optional] Returns preferred identifier for i-th parameter.
template PARAMETER_VARIABLE_ID(size_t i);
// Returns the identifier of the FuncInfo instance for the i-th overload
// of the specified name. The identifier must be accessible in the scope
// where generated code is mixed.
template FUNCINFO_ID(string name, size_t i);
// Returns implemented function body as a string. When WITHOUT_SYMBOL is
// defined, the latter is used.
template generateFunctionBody(alias func);
template generateFunctionBody(string name, FuncType);
}
--------------------
*/
package template MemberFunctionGenerator(alias Policy)
{
private static:
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Internal stuffs
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
import std.format;
enum CONSTRUCTOR_NAME = "__ctor";
// true if functions are derived from a base class
enum WITH_BASE_CLASS = __traits(hasMember, Policy, "BASE_CLASS_ID");
// true if functions are specified as types, not symbols
enum WITHOUT_SYMBOL = __traits(hasMember, Policy, "WITHOUT_SYMBOL");
// preferred identifier for i-th parameter variable
static if (__traits(hasMember, Policy, "PARAMETER_VARIABLE_ID"))
{
alias PARAMETER_VARIABLE_ID = Policy.PARAMETER_VARIABLE_ID;
}
else
{
enum string PARAMETER_VARIABLE_ID(size_t i) = format("a%s", i);
// default: a0, a1, ...
}
// Returns a tuple consisting of 0,1,2,...,n-1. For static foreach.
template CountUp(size_t n)
{
static if (n > 0)
alias CountUp = TypeTuple!(CountUp!(n - 1), n - 1);
else
alias CountUp = TypeTuple!();
}
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
// Code generator
//:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
/*
* Runs through all the target overload sets and generates D code which
* implements all the functions in the overload sets.
*/
public string generateCode(overloads...)() @property
{
string code = "";
// run through all the overload sets
foreach (i_; CountUp!(0 + overloads.length)) // workaround
{
enum i = 0 + i_; // workaround
alias oset = overloads[i];
code ~= generateCodeForOverloadSet!(oset);
static if (WITH_BASE_CLASS && oset.name != CONSTRUCTOR_NAME)
{
// The generated function declarations may hide existing ones
// in the base class (cf. HiddenFuncError), so we put an alias
// declaration here to reveal possible hidden functions.
code ~= format("alias %s = %s.%s;\n",
oset.name,
Policy.BASE_CLASS_ID, // [BUG 2540] super.
oset.name);
}
}
return code;
}
// handle each overload set
private string generateCodeForOverloadSet(alias oset)() @property
{
string code = "";
foreach (i_; CountUp!(0 + oset.contents.length)) // workaround
{
enum i = 0 + i_; // workaround
code ~= generateFunction!(
Policy.FUNCINFO_ID!(oset.name, i), oset.name,
oset.contents[i]) ~ "\n";
}
return code;
}
/*
* Returns D code which implements the function func. This function
* actually generates only the declarator part; the function body part is
* generated by the functionGenerator() policy.
*/
public string generateFunction(
string myFuncInfo, string name, func... )() @property
{
import std.format : format;
enum isCtor = (name == CONSTRUCTOR_NAME);
string code; // the result
auto paramsRes = generateParameters!(myFuncInfo, func)();
code ~= paramsRes.imports;
/*** Function Declarator ***/
{
alias Func = FunctionTypeOf!(func);
alias FA = FunctionAttribute;
enum atts = functionAttributes!(func);
enum realName = isCtor ? "this" : name;
// FIXME?? Make it so that these aren't CTFE funcs any more, since
// Format is deprecated, and format works at compile time?
/* Made them CTFE funcs just for the sake of Format!(...) */
// return type with optional "ref"
static string make_returnType()
{
string rtype = "";
if (!isCtor)
{
if (atts & FA.ref_) rtype ~= "ref ";
rtype ~= myFuncInfo ~ ".RT";
}
return rtype;
}
enum returnType = make_returnType();
// function attributes attached after declaration
static string make_postAtts()
{
string poatts = "";
if (atts & FA.pure_ ) poatts ~= " pure";
if (atts & FA.nothrow_) poatts ~= " nothrow";
if (atts & FA.property) poatts ~= " @property";
if (atts & FA.safe ) poatts ~= " @safe";
if (atts & FA.trusted ) poatts ~= " @trusted";
return poatts;
}
enum postAtts = make_postAtts();
// function storage class
static string make_storageClass()
{
string postc = "";
if (is(Func == shared)) postc ~= " shared";
if (is(Func == const)) postc ~= " const";
if (is(Func == inout)) postc ~= " inout";
if (is(Func == immutable)) postc ~= " immutable";
return postc;
}
enum storageClass = make_storageClass();
//
if (__traits(isVirtualMethod, func))
code ~= "override ";
code ~= format("extern(%s) %s %s(%s) %s %s\n",
functionLinkage!(func),
returnType,
realName,
paramsRes.params,
postAtts, storageClass );
}
/*** Function Body ***/
code ~= "{\n";
{
enum nparams = ParameterTypeTuple!(func).length;
/* Declare keywords: args, self and parent. */
string preamble;
preamble ~= "alias args = TypeTuple!(" ~ enumerateParameters!(nparams) ~ ");\n";
if (!isCtor)
{
preamble ~= "alias self = " ~ name ~ ";\n";
if (WITH_BASE_CLASS && !__traits(isAbstractFunction, func))
//preamble ~= "alias super." ~ name ~ " parent;\n"; // [BUG 2540]
preamble ~= "auto parent = &super." ~ name ~ ";\n";
}
// Function body
static if (WITHOUT_SYMBOL)
enum fbody = Policy.generateFunctionBody!(name, func);
else
enum fbody = Policy.generateFunctionBody!(func);
code ~= preamble;
code ~= fbody;
}
code ~= "}";
return code;
}
/*
* Returns D code which declares function parameters,
* and optionally any imports (e.g. core.vararg)
* "ref int a0, real a1, ..."
*/
static struct GenParams { string imports, params; }
private GenParams generateParameters(string myFuncInfo, func...)()
{
alias STC = ParameterStorageClass;
alias stcs = ParameterStorageClassTuple!(func);
enum nparams = stcs.length;
string imports = ""; // any imports required
string params = ""; // parameters
foreach (i, stc; stcs)
{
if (i > 0) params ~= ", ";
// Parameter storage classes.
if (stc & STC.scope_) params ~= "scope ";
if (stc & STC.out_ ) params ~= "out ";
if (stc & STC.ref_ ) params ~= "ref ";
if (stc & STC.lazy_ ) params ~= "lazy ";
// Take parameter type from the FuncInfo.
params ~= format("%s.PT[%s]", myFuncInfo, i);
// Declare a parameter variable.
params ~= " " ~ PARAMETER_VARIABLE_ID!(i);
}
// Add some ellipsis part if needed.
auto style = variadicFunctionStyle!(func);
final switch (style)
{
case Variadic.no:
break;
case Variadic.c, Variadic.d:
imports ~= "import core.vararg;\n";
// (...) or (a, b, ...)
params ~= (nparams == 0) ? "..." : ", ...";
break;
case Variadic.typesafe:
params ~= " ...";
break;
}
return typeof(return)(imports, params);
}
// Returns D code which enumerates n parameter variables using comma as the
// separator. "a0, a1, a2, a3"
private string enumerateParameters(size_t n)() @property
{
string params = "";
foreach (i_; CountUp!(n))
{
enum i = 0 + i_; // workaround
if (i > 0) params ~= ", ";
params ~= PARAMETER_VARIABLE_ID!(i);
}
return params;
}
}
/**
Predefined how-policies for $(D AutoImplement). These templates are used by
$(D BlackHole) and $(D WhiteHole), respectively.
*/
template generateEmptyFunction(C, func.../+[BUG 4217]+/)
{
static if (is(ReturnType!(func) == void))
enum string generateEmptyFunction = q{
};
else static if (functionAttributes!(func) & FunctionAttribute.ref_)
enum string generateEmptyFunction = q{
static typeof(return) dummy;
return dummy;
};
else
enum string generateEmptyFunction = q{
return typeof(return).init;
};
}
/// ditto
template generateAssertTrap(C, func...)
{
enum string generateAssertTrap =
`throw new NotImplementedError("` ~ C.stringof ~ "."
~ __traits(identifier, func) ~ `");`;
}
private
{
pragma(mangle, "_d_toObject")
extern(C) pure nothrow Object typecons_d_toObject(void* p);
}
/*
* Avoids opCast operator overloading.
*/
private template dynamicCast(T)
if (is(T == class) || is(T == interface))
{
@trusted
T dynamicCast(S)(inout S source)
if (is(S == class) || is(S == interface))
{
static if (is(Unqual!S : Unqual!T))
{
import std.traits : QualifierOf;
alias Qual = QualifierOf!S; // SharedOf or MutableOf
alias TmpT = Qual!(Unqual!T);
inout(TmpT) tmp = source; // bypass opCast by implicit conversion
return *cast(T*)(&tmp); // + variable pointer cast + dereference
}
else
{
return cast(T)typecons_d_toObject(*cast(void**)(&source));
}
}
}
unittest
{
class C { @disable opCast(T)() {} }
auto c = new C;
static assert(!__traits(compiles, cast(Object)c));
auto o = dynamicCast!Object(c);
assert(c is o);
interface I { @disable opCast(T)() {} Object instance(); }
interface J { @disable opCast(T)() {} Object instance(); }
class D : I, J { Object instance() { return this; } }
I i = new D();
static assert(!__traits(compiles, cast(J)i));
J j = dynamicCast!J(i);
assert(i.instance() is j.instance());
}
/**
* Supports structural based typesafe conversion.
*
* If $(D Source) has structural conformance with the $(D interface) $(D Targets),
* wrap creates internal wrapper class which inherits $(D Targets) and
* wrap $(D src) object, then return it.
*/
template wrap(Targets...)
if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
{
import std.typetuple : staticMap;
// strict upcast
auto wrap(Source)(inout Source src) @trusted pure nothrow
if (Targets.length == 1 && is(Source : Targets[0]))
{
alias T = Select!(is(Source == shared), shared Targets[0], Targets[0]);
return dynamicCast!(inout T)(src);
}
// structural upcast
template wrap(Source)
if (!allSatisfy!(Bind!(isImplicitlyConvertible, Source), Targets))
{
auto wrap(inout Source src)
{
static assert(hasRequireMethods!(),
"Source "~Source.stringof~
" does not have structural conformance to "~
Targets.stringof);
alias T = Select!(is(Source == shared), shared Impl, Impl);
return new inout T(src);
}
template FuncInfo(string s, F)
{
enum name = s;
alias type = F;
}
// Concat all Targets function members into one tuple
template Concat(size_t i = 0)
{
static if (i >= Targets.length)
alias Concat = TypeTuple!();
else
{
alias Concat = TypeTuple!(GetOverloadedMethods!(Targets[i]), Concat!(i + 1));
}
}
// Remove duplicated functions based on the identifier name and function type covariance
template Uniq(members...)
{
static if (members.length == 0)
alias Uniq = TypeTuple!();
else
{
alias func = members[0];
enum name = __traits(identifier, func);
alias type = FunctionTypeOf!func;
template check(size_t i, mem...)
{
static if (i >= mem.length)
enum ptrdiff_t check = -1;
else
{
enum ptrdiff_t check =
__traits(identifier, func) == __traits(identifier, mem[i]) &&
!is(DerivedFunctionType!(type, FunctionTypeOf!(mem[i])) == void)
? i : check!(i + 1, mem);
}
}
enum ptrdiff_t x = 1 + check!(0, members[1 .. $]);
static if (x >= 1)
{
alias typex = DerivedFunctionType!(type, FunctionTypeOf!(members[x]));
alias remain = Uniq!(members[1 .. x], members[x + 1 .. $]);
static if (remain.length >= 1 && remain[0].name == name &&
!is(DerivedFunctionType!(typex, remain[0].type) == void))
{
alias F = DerivedFunctionType!(typex, remain[0].type);
alias Uniq = TypeTuple!(FuncInfo!(name, F), remain[1 .. $]);
}
else
alias Uniq = TypeTuple!(FuncInfo!(name, typex), remain);
}
else
{
alias Uniq = TypeTuple!(FuncInfo!(name, type), Uniq!(members[1 .. $]));
}
}
}
alias TargetMembers = Uniq!(Concat!()); // list of FuncInfo
alias SourceMembers = GetOverloadedMethods!Source; // list of function symbols
// Check whether all of SourceMembers satisfy covariance target in TargetMembers
template hasRequireMethods(size_t i = 0)
{
static if (i >= TargetMembers.length)
enum hasRequireMethods = true;
else
{
enum hasRequireMethods =
findCovariantFunction!(TargetMembers[i], Source, SourceMembers) != -1 &&
hasRequireMethods!(i + 1);
}
}
// Internal wrapper class
final class Impl : Structural, Targets
{
private:
Source _wrap_source;
this( inout Source s) inout @safe pure nothrow { _wrap_source = s; }
this(shared inout Source s) shared inout @safe pure nothrow { _wrap_source = s; }
// BUG: making private should work with NVI.
protected final inout(Object) _wrap_getSource() inout @trusted
{
return dynamicCast!(inout Object)(_wrap_source);
}
import std.conv : to;
import std.algorithm : forward;
template generateFun(size_t i)
{
enum name = TargetMembers[i].name;
enum fa = functionAttributes!(TargetMembers[i].type);
static @property stc()
{
string r;
if (fa & FunctionAttribute.property) r ~= "@property ";
if (fa & FunctionAttribute.ref_) r ~= "ref ";
if (fa & FunctionAttribute.pure_) r ~= "pure ";
if (fa & FunctionAttribute.nothrow_) r ~= "nothrow ";
if (fa & FunctionAttribute.trusted) r ~= "@trusted ";
if (fa & FunctionAttribute.safe) r ~= "@safe ";
return r;
}
static @property mod()
{
alias type = TypeTuple!(TargetMembers[i].type)[0];
string r;
static if (is(type == immutable)) r ~= " immutable";
else
{
static if (is(type == shared)) r ~= " shared";
static if (is(type == const)) r ~= " const";
else static if (is(type == inout)) r ~= " inout";
//else --> mutable
}
return r;
}
enum n = to!string(i);
static if (fa & FunctionAttribute.property)
{
static if (ParameterTypeTuple!(TargetMembers[i].type).length == 0)
enum fbody = "_wrap_source."~name;
else
enum fbody = "_wrap_source."~name~" = forward!args";
}
else
{
enum fbody = "_wrap_source."~name~"(forward!args)";
}
enum generateFun =
"override "~stc~"ReturnType!(TargetMembers["~n~"].type) "
~ name~"(ParameterTypeTuple!(TargetMembers["~n~"].type) args) "~mod~
"{ return "~fbody~"; }";
}
public:
mixin mixinAll!(
staticMap!(generateFun, staticIota!(0, TargetMembers.length)));
}
}
}
/// ditto
template wrap(Targets...)
if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))
{
import std.typetuple : staticMap;
alias wrap = .wrap!(staticMap!(Unqual, Targets));
}
// Internal class to support dynamic cross-casting
private interface Structural
{
inout(Object) _wrap_getSource() inout @safe pure nothrow;
}
/**
* Extract object which wrapped by $(D wrap).
*/
template unwrap(Target)
if (isMutable!Target)
{
// strict downcast
auto unwrap(Source)(inout Source src) @trusted pure nothrow
if (is(Target : Source))
{
alias T = Select!(is(Source == shared), shared Target, Target);
return dynamicCast!(inout T)(src);
}
// structural downcast
auto unwrap(Source)(inout Source src) @trusted pure nothrow
if (!is(Target : Source))
{
alias T = Select!(is(Source == shared), shared Target, Target);
Object o = dynamicCast!(Object)(src); // remove qualifier
do
{
if (auto a = dynamicCast!(Structural)(o))
{
if (auto d = dynamicCast!(inout T)(o = a._wrap_getSource()))
return d;
}
else if (auto d = dynamicCast!(inout T)(o))
return d;
else
break;
} while (o);
return null;
}
}
/// ditto
template unwrap(Target)
if (!isMutable!Target)
{
alias unwrap = .unwrap!(Unqual!Target);
}
///
unittest
{
interface Quack
{
int quack();
@property int height();
}
interface Flyer
{
@property int height();
}
class Duck : Quack
{
int quack() { return 1; }
@property int height() { return 10; }
}
class Human
{
int quack() { return 2; }
@property int height() { return 20; }
}
Duck d1 = new Duck();
Human h1 = new Human();
interface Refleshable
{
int reflesh();
}
// does not have structural conformance
static assert(!__traits(compiles, d1.wrap!Refleshable));
static assert(!__traits(compiles, h1.wrap!Refleshable));
// strict upcast
Quack qd = d1.wrap!Quack;
assert(qd is d1);
assert(qd.quack() == 1); // calls Duck.quack
// strict downcast
Duck d2 = qd.unwrap!Duck;
assert(d2 is d1);
// structural upcast
Quack qh = h1.wrap!Quack;
assert(qh.quack() == 2); // calls Human.quack
// structural downcast
Human h2 = qh.unwrap!Human;
assert(h2 is h1);
// structural upcast (two steps)
Quack qx = h1.wrap!Quack; // Human -> Quack
Flyer fx = qx.wrap!Flyer; // Quack -> Flyer
assert(fx.height == 20); // calls Human.height
// strucural downcast (two steps)
Quack qy = fx.unwrap!Quack; // Flyer -> Quack
Human hy = qy.unwrap!Human; // Quack -> Human
assert(hy is h1);
// strucural downcast (one step)
Human hz = fx.unwrap!Human; // Flyer -> Human
assert(hz is h1);
}
///
unittest
{
interface A { int run(); }
interface B { int stop(); @property int status(); }
class X
{
int run() { return 1; }
int stop() { return 2; }
@property int status() { return 3; }
}
auto x = new X();
auto ab = x.wrap!(A, B);
A a = ab;
B b = ab;
assert(a.run() == 1);
assert(b.stop() == 2);
assert(b.status == 3);
static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
}
unittest
{
class A
{
int draw() { return 1; }
int draw(int v) { return v; }
int draw() const { return 2; }
int draw() shared { return 3; }
int draw() shared const { return 4; }
int draw() immutable { return 5; }
}
interface Drawable
{
int draw();
int draw() const;
int draw() shared;
int draw() shared const;
int draw() immutable;
}
interface Drawable2
{
int draw(int v);
}
auto ma = new A();
auto sa = new shared A();
auto ia = new immutable A();
{
Drawable md = ma.wrap!Drawable;
const Drawable cd = ma.wrap!Drawable;
shared Drawable sd = sa.wrap!Drawable;
shared const Drawable scd = sa.wrap!Drawable;
immutable Drawable id = ia.wrap!Drawable;
assert( md.draw() == 1);
assert( cd.draw() == 2);
assert( sd.draw() == 3);
assert(scd.draw() == 4);
assert( id.draw() == 5);
}
{
Drawable2 d = ma.wrap!Drawable2;
static assert(!__traits(compiles, d.draw()));
assert(d.draw(10) == 10);
}
}
unittest
{
// Bugzilla 10377
import std.range, std.algorithm;
interface MyInputRange(T)
{
@property T front();
void popFront();
@property bool empty();
}
//auto o = iota(0,10,1).inputRangeObject();
//pragma(msg, __traits(allMembers, typeof(o)));
auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)();
assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
}
unittest
{
// Bugzilla 10536
interface Interface
{
int foo();
}
class Pluggable
{
int foo() { return 1; }
@disable void opCast(T, this X)(); // !
}
Interface i = new Pluggable().wrap!Interface;
assert(i.foo() == 1);
}
unittest
{
// Enhancement 10538
interface Interface
{
int foo();
int bar(int);
}
class Pluggable
{
int opDispatch(string name, A...)(A args) { return 100; }
}
Interface i = wrap!Interface(new Pluggable());
assert(i.foo() == 100);
assert(i.bar(10) == 100);
}
// Make a tuple of non-static function symbols
private template GetOverloadedMethods(T)
{
import std.typetuple : Filter;
alias allMembers = TypeTuple!(__traits(allMembers, T));
template follows(size_t i = 0)
{
static if (i >= allMembers.length)
{
alias follows = TypeTuple!();
}
else static if (!__traits(compiles, mixin("T."~allMembers[i])))
{
alias follows = follows!(i + 1);
}
else
{
enum name = allMembers[i];
template isMethod(alias f)
{
static if (is(typeof(&f) F == F*) && is(F == function))
enum isMethod = !__traits(isStaticFunction, f);
else
enum isMethod = false;
}
alias follows = TypeTuple!(
std.typetuple.Filter!(isMethod, __traits(getOverloads, T, name)),
follows!(i + 1));
}
}
alias GetOverloadedMethods = follows!();
}
// find a function from Fs that has same identifier and covariant type with f
private template findCovariantFunction(alias finfo, Source, Fs...)
{
template check(size_t i = 0)
{
static if (i >= Fs.length)
enum ptrdiff_t check = -1;
else
{
enum ptrdiff_t check =
(finfo.name == __traits(identifier, Fs[i])) &&
isCovariantWith!(FunctionTypeOf!(Fs[i]), finfo.type)
? i : check!(i + 1);
}
}
enum x = check!();
static if (x == -1 && is(typeof(Source.opDispatch)))
{
alias Params = ParameterTypeTuple!(finfo.type);
enum ptrdiff_t findCovariantFunction =
is(typeof(( Source).init.opDispatch!(finfo.name)(Params.init))) ||
is(typeof(( const Source).init.opDispatch!(finfo.name)(Params.init))) ||
is(typeof(( immutable Source).init.opDispatch!(finfo.name)(Params.init))) ||
is(typeof(( shared Source).init.opDispatch!(finfo.name)(Params.init))) ||
is(typeof((shared const Source).init.opDispatch!(finfo.name)(Params.init)))
? ptrdiff_t.max : -1;
}
else
enum ptrdiff_t findCovariantFunction = x;
}
private enum TypeModifier
{
mutable = 0, // type is mutable
const_ = 1, // type is const
immutable_ = 2, // type is immutable
shared_ = 4, // type is shared
inout_ = 8, // type is wild
}
private template TypeMod(T)
{
static if (is(T == immutable))
{
enum mod1 = TypeModifier.immutable_;
enum mod2 = 0;
}
else
{
enum mod1 = is(T == shared) ? TypeModifier.shared_ : 0;
static if (is(T == const))
enum mod2 = TypeModifier.const_;
else static if (is(T == inout))
enum mod2 = TypeModifier.inout_;
else
enum mod2 = TypeModifier.mutable;
}
enum TypeMod = cast(TypeModifier)(mod1 | mod2);
}
version(unittest)
{
private template UnittestFuncInfo(alias f)
{
enum name = __traits(identifier, f);
alias type = FunctionTypeOf!f;
}
}
unittest
{
class A
{
int draw() { return 1; }
@property int value() { return 2; }
final int run() { return 3; }
}
alias methods = GetOverloadedMethods!A;
alias int F1();
alias @property int F2();
alias string F3();
alias nothrow @trusted uint F4();
alias int F5(Object);
alias bool F6(Object);
static assert(methods.length == 3 + 4);
static assert(__traits(identifier, methods[0]) == "draw" && is(typeof(&methods[0]) == F1*));
static assert(__traits(identifier, methods[1]) == "value" && is(typeof(&methods[1]) == F2*));
static assert(__traits(identifier, methods[2]) == "run" && is(typeof(&methods[2]) == F1*));
int draw() { return 0; }
@property int value() { return 0; }
void opEquals() {}
int nomatch() { return 0; }
static assert(findCovariantFunction!(UnittestFuncInfo!draw, A, methods) == 0);
static assert(findCovariantFunction!(UnittestFuncInfo!value, A, methods) == 1);
static assert(findCovariantFunction!(UnittestFuncInfo!opEquals, A, methods) == -1);
static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, A, methods) == -1);
// considering opDispatch
class B
{
void opDispatch(string name, A...)(A) {}
}
alias methodsB = GetOverloadedMethods!B;
static assert(findCovariantFunction!(UnittestFuncInfo!draw, B, methodsB) == ptrdiff_t.max);
static assert(findCovariantFunction!(UnittestFuncInfo!value, B, methodsB) == ptrdiff_t.max);
static assert(findCovariantFunction!(UnittestFuncInfo!opEquals, B, methodsB) == ptrdiff_t.max);
static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, B, methodsB) == ptrdiff_t.max);
}
private template DerivedFunctionType(T...)
{
static if (!T.length)
{
alias DerivedFunctionType = void;
}
else static if (T.length == 1)
{
static if (is(T[0] == function))
{
alias DerivedFunctionType = T[0];
}
else
{
alias DerivedFunctionType = void;
}
}
else static if (is(T[0] P0 == function) && is(T[1] P1 == function))
{
alias FA = FunctionAttribute;
alias F0 = T[0], R0 = ReturnType!F0, PSTC0 = ParameterStorageClassTuple!F0;
alias F1 = T[1], R1 = ReturnType!F1, PSTC1 = ParameterStorageClassTuple!F1;
enum FA0 = functionAttributes!F0;
enum FA1 = functionAttributes!F1;
template CheckParams(size_t i = 0)
{
static if (i >= P0.length)
enum CheckParams = true;
else
{
enum CheckParams = (is(P0[i] == P1[i]) && PSTC0[i] == PSTC1[i]) &&
CheckParams!(i + 1);
}
}
static if (R0.sizeof == R1.sizeof && !is(CommonType!(R0, R1) == void) &&
P0.length == P1.length && CheckParams!() && TypeMod!F0 == TypeMod!F1 &&
variadicFunctionStyle!F0 == variadicFunctionStyle!F1 &&
functionLinkage!F0 == functionLinkage!F1 &&
((FA0 ^ FA1) & (FA.ref_ | FA.property)) == 0)
{
alias R = Select!(is(R0 : R1), R0, R1);
alias FX = FunctionTypeOf!(R function(P0));
// @system is default
alias FY = SetFunctionAttributes!(FX, functionLinkage!F0, (FA0 | FA1) & ~FA.system);
alias DerivedFunctionType = DerivedFunctionType!(FY, T[2 .. $]);
}
else
alias DerivedFunctionType = void;
}
else
alias DerivedFunctionType = void;
}
unittest
{
// attribute covariance
alias int F1();
static assert(is(DerivedFunctionType!(F1, F1) == F1));
alias int F2() pure nothrow;
static assert(is(DerivedFunctionType!(F1, F2) == F2));
alias int F3() @safe;
alias int F23() @safe pure nothrow;
static assert(is(DerivedFunctionType!(F2, F3) == F23));
// return type covariance
alias long F4();
static assert(is(DerivedFunctionType!(F1, F4) == void));
class C {}
class D : C {}
alias C F5();
alias D F6();
static assert(is(DerivedFunctionType!(F5, F6) == F6));
alias typeof(null) F7();
alias int[] F8();
alias int* F9();
static assert(is(DerivedFunctionType!(F5, F7) == F7));
static assert(is(DerivedFunctionType!(F7, F8) == void));
static assert(is(DerivedFunctionType!(F7, F9) == F7));
// variadic type equality
alias int F10(int);
alias int F11(int...);
alias int F12(int, ...);
static assert(is(DerivedFunctionType!(F10, F11) == void));
static assert(is(DerivedFunctionType!(F10, F12) == void));
static assert(is(DerivedFunctionType!(F11, F12) == void));
// linkage equality
alias extern(C) int F13(int);
alias extern(D) int F14(int);
alias extern(Windows) int F15(int);
static assert(is(DerivedFunctionType!(F13, F14) == void));
static assert(is(DerivedFunctionType!(F13, F15) == void));
static assert(is(DerivedFunctionType!(F14, F15) == void));
// ref & @property equality
alias int F16(int);
alias ref int F17(int);
alias @property int F18(int);
static assert(is(DerivedFunctionType!(F16, F17) == void));
static assert(is(DerivedFunctionType!(F16, F18) == void));
static assert(is(DerivedFunctionType!(F17, F18) == void));
}
package template staticIota(int beg, int end)
{
static if (beg + 1 >= end)
{
static if (beg >= end)
{
alias staticIota = TypeTuple!();
}
else
{
alias staticIota = TypeTuple!(+beg);
}
}
else
{
enum mid = beg + (end - beg) / 2;
alias staticIota = TypeTuple!(staticIota!(beg, mid), staticIota!(mid, end));
}
}
private template mixinAll(mixins...)
{
static if (mixins.length == 1)
{
static if (is(typeof(mixins[0]) == string))
{
mixin(mixins[0]);
}
else
{
alias it = mixins[0];
mixin it;
}
}
else static if (mixins.length >= 2)
{
mixin mixinAll!(mixins[ 0 .. $/2]);
mixin mixinAll!(mixins[$/2 .. $ ]);
}
}
private template Bind(alias Template, args1...)
{
alias Bind(args2...) = Template!(args1, args2);
}
/**
Options regarding auto-initialization of a $(D RefCounted) object (see
the definition of $(D RefCounted) below).
*/
enum RefCountedAutoInitialize
{
/// Do not auto-initialize the object
no,
/// Auto-initialize the object
yes,
}
/**
Defines a reference-counted object containing a $(D T) value as
payload. $(D RefCounted) keeps track of all references of an object,
and when the reference count goes down to zero, frees the underlying
store. $(D RefCounted) uses $(D malloc) and $(D free) for operation.
$(D RefCounted) is unsafe and should be used with care. No references
to the payload should be escaped outside the $(D RefCounted) object.
The $(D autoInit) option makes the object ensure the store is
automatically initialized. Leaving $(D autoInit ==
RefCountedAutoInitialize.yes) (the default option) is convenient but
has the cost of a test whenever the payload is accessed. If $(D
autoInit == RefCountedAutoInitialize.no), user code must call either
$(D refCountedStore.isInitialized) or $(D refCountedStore.ensureInitialized)
before attempting to access the payload. Not doing so results in null
pointer dereference.
Example:
----
// A pair of an $(D int) and a $(D size_t) - the latter being the
// reference count - will be dynamically allocated
auto rc1 = RefCounted!int(5);
assert(rc1 == 5);
// No more allocation, add just one extra reference count
auto rc2 = rc1;
// Reference semantics
rc2 = 42;
assert(rc1 == 42);
// the pair will be freed when rc1 and rc2 go out of scope
----
*/
struct RefCounted(T, RefCountedAutoInitialize autoInit =
RefCountedAutoInitialize.yes)
if (!is(T == class))
{
/// $(D RefCounted) storage implementation.
struct RefCountedStore
{
private struct Impl
{
T _payload;
size_t _count;
}
private Impl* _store;
private void initialize(A...)(auto ref A args)
{
import core.memory : GC;
import core.stdc.stdlib : malloc;
import std.conv : emplace;
import std.exception : enforce;
_store = cast(Impl*) enforce(malloc(Impl.sizeof));
static if (hasIndirections!T)
GC.addRange(&_store._payload, T.sizeof);
emplace(&_store._payload, args);
_store._count = 1;
}
/**
Returns $(D true) if and only if the underlying store has been
allocated and initialized.
*/
@property nothrow @safe
bool isInitialized() const
{
return _store !is null;
}
/**
Returns underlying reference count if it is allocated and initialized
(a positive integer), and $(D 0) otherwise.
*/
@property nothrow @safe
size_t refCount() const
{
return isInitialized ? _store._count : 0;
}
/**
Makes sure the payload was properly initialized. Such a
call is typically inserted before using the payload.
*/
void ensureInitialized()
{
if (!isInitialized) initialize();
}
}
RefCountedStore _refCounted;
/// Returns storage implementation struct.
@property nothrow @safe
ref inout(RefCountedStore) refCountedStore() inout
{
return _refCounted;
}
/**
Constructor that initializes the payload.
Postcondition: $(D refCountedStore.isInitialized)
*/
this(A...)(auto ref A args) if (A.length > 0)
{
_refCounted.initialize(args);
}
/**
Constructor that tracks the reference count appropriately. If $(D
!refCountedStore.isInitialized), does nothing.
*/
this(this)
{
if (!_refCounted.isInitialized) return;
++_refCounted._store._count;
}
/**
Destructor that tracks the reference count appropriately. If $(D
!refCountedStore.isInitialized), does nothing. When the reference count goes
down to zero, calls $(D destroy) agaist the payload and calls $(D free)
to deallocate the corresponding resource.
*/
~this()
{
if (!_refCounted.isInitialized) return;
assert(_refCounted._store._count > 0);
if (--_refCounted._store._count)
return;
// Done, deallocate
.destroy(_refCounted._store._payload);
static if (hasIndirections!T)
{
import core.memory : GC;
GC.removeRange(&_refCounted._store._payload);
}
import core.stdc.stdlib : free;
free(_refCounted._store);
_refCounted._store = null;
}
/**
Assignment operators
*/
void opAssign(typeof(this) rhs)
{
import std.algorithm : swap;
swap(_refCounted._store, rhs._refCounted._store);
}
/// Ditto
void opAssign(T rhs)
{
import std.algorithm : move;
static if (autoInit == RefCountedAutoInitialize.yes)
{
_refCounted.ensureInitialized();
}
else
{
assert(_refCounted.isInitialized);
}
move(rhs, _refCounted._store._payload);
}
//version to have a single properly ddoc'ed function (w/ correct sig)
version(StdDdoc)
{
/**
Returns a reference to the payload. If (autoInit ==
RefCountedAutoInitialize.yes), calls $(D
refCountedStore.ensureInitialized). Otherwise, just issues $(D
assert(refCountedStore.isInitialized)). Used with $(D alias
refCountedPayload this;), so callers can just use the $(D RefCounted)
object as a $(D T).
$(BLUE The first overload exists only if $(D autoInit == RefCountedAutoInitialize.yes).)
So if $(D autoInit == RefCountedAutoInitialize.no)
or called for a constant or immutable object, then
$(D refCountedPayload) will also be qualified as safe and nothrow
(but will still assert if not initialized).
*/
@property
ref T refCountedPayload();
/// ditto
@property nothrow @safe
ref inout(T) refCountedPayload() inout;
}
else
{
static if (autoInit == RefCountedAutoInitialize.yes)
{
//Can't use inout here because of potential mutation
@property
ref T refCountedPayload()
{
_refCounted.ensureInitialized();
return _refCounted._store._payload;
}
}
@property nothrow @safe
ref inout(T) refCountedPayload() inout
{
assert(_refCounted.isInitialized, "Attempted to access an uninitialized payload.");
return _refCounted._store._payload;
}
}
/**
Returns a reference to the payload. If (autoInit ==
RefCountedAutoInitialize.yes), calls $(D
refCountedStore.ensureInitialized). Otherwise, just issues $(D
assert(refCountedStore.isInitialized)).
*/
alias refCountedPayload this;
}
unittest
{
RefCounted!int* p;
{
auto rc1 = RefCounted!int(5);
p = &rc1;
assert(rc1 == 5);
assert(rc1._refCounted._store._count == 1);
auto rc2 = rc1;
assert(rc1._refCounted._store._count == 2);
// Reference semantics
rc2 = 42;
assert(rc1 == 42);
rc2 = rc2;
assert(rc2._refCounted._store._count == 2);
rc1 = rc2;
assert(rc1._refCounted._store._count == 2);
}
assert(p._refCounted._store == null);
// RefCounted as a member
struct A
{
RefCounted!int x;
this(int y)
{
x._refCounted.initialize(y);
}
A copy()
{
auto another = this;
return another;
}
}
auto a = A(4);
auto b = a.copy();
assert(a.x._refCounted._store._count == 2, "BUG 4356 still unfixed");
}
unittest
{
import std.algorithm : swap;
RefCounted!int p1, p2;
swap(p1, p2);
}
// 6606
unittest
{
union U {
size_t i;
void* p;
}
struct S {
U u;
}
alias SRC = RefCounted!S;
}
// 6436
unittest
{
struct S { this(ref int val) { assert(val == 3); ++val; } }
int val = 3;
auto s = RefCounted!S(val);
assert(val == 4);
}
unittest
{
RefCounted!int a;
a = 5; //This should not assert
assert(a == 5);
RefCounted!int b;
b = a; //This should not assert either
assert(b == 5);
}
/**
Make proxy for $(D a).
Example:
----
struct MyInt
{
private int value;
mixin Proxy!value;
this(int n){ value = n; }
}
MyInt n = 10;
// Enable operations that original type has.
++n;
assert(n == 11);
assert(n * 2 == 22);
void func(int n) { }
// Disable implicit conversions to original type.
//int x = n;
//func(n);
----
*/
mixin template Proxy(alias a)
{
static if (is(typeof(this) == class))
{
override bool opEquals(Object o)
{
if (auto b = cast(typeof(this))o)
{
import std.algorithm : startsWith;
static assert(startsWith(a.stringof, "this."));
return a == mixin("b."~a.stringof[5..$]); // remove "this."
}
return false;
}
bool opEquals(T)(T b)
if (is(typeof(a):T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a))))
{
static if (is(typeof(a.opEquals(b))))
return a.opEquals(b);
else static if (is(typeof(b.opEquals(a))))
return b.opEquals(a);
else
return a == b;
}
override int opCmp(Object o)
{
if (auto b = cast(typeof(this))o)
{
import std.algorithm : startsWith;
static assert(startsWith(a.stringof, "this.")); // remove "this."
return a < mixin("b."~a.stringof[5..$]) ? -1
: a > mixin("b."~a.stringof[5..$]) ? +1 : 0;
}
static if (is(typeof(a) == class))
return a.opCmp(o);
else
throw new Exception("Attempt to compare a "~typeid(this).toString~" and a "~typeid(o).toString);
}
int opCmp(T)(auto ref const T b)
if (is(typeof(a):T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a))))
{
static if (is(typeof(a.opCmp(b))))
return a.opCmp(b);
else static if (is(typeof(b.opCmp(a))))
return -b.opCmp(b);
else
return a < b ? -1 : a > b ? +1 : 0;
}
override hash_t toHash() const nothrow @trusted
{
return typeid(typeof(a)).getHash(cast(const void*)&a);
}
}
else
{
auto ref opEquals(this X, B)(auto ref B b)
{
static if (is(immutable B == immutable typeof(this)))
{
import std.algorithm : startsWith;
static assert(startsWith(a.stringof, "this."));
return a == mixin("b."~a.stringof[5..$]); // remove "this."
}
else
return a == b;
}
auto ref opCmp(this X, B)(auto ref B b)
if (!is(typeof(a.opCmp(b))) || !is(typeof(b.opCmp(a))))
{
static if (is(typeof(a.opCmp(b))))
return a.opCmp(b);
else static if (is(typeof(b.opCmp(a))))
return -b.opCmp(a);
else
return a < b ? -1 : a > b ? +1 : 0;
}
hash_t toHash() const nothrow @trusted
{
return typeid(typeof(a)).getHash(cast(const void*)&a);
}
}
auto ref opCall(this X, Args...)(auto ref Args args) { return a(args); }
auto ref opCast(T, this X)() { return cast(T)a; }
auto ref opIndex(this X, D...)(auto ref D i) { return a[i]; }
auto ref opSlice(this X )() { return a[]; }
auto ref opSlice(this X, B, E)(auto ref B b, auto ref E e) { return a[b..e]; }
auto ref opUnary (string op, this X )() { return mixin(op~"a"); }
auto ref opIndexUnary(string op, this X, D...)(auto ref D i) { return mixin(op~"a[i]"); }
auto ref opSliceUnary(string op, this X )() { return mixin(op~"a[]"); }
auto ref opSliceUnary(string op, this X, B, E)(auto ref B b, auto ref E e) { return mixin(op~"a[b..e]"); }
auto ref opBinary(string op, this X, B)(auto ref B b)
if (op == "in" && is(typeof(a in b)) || op != "in")
{
return mixin("a "~op~" b");
}
auto ref opBinaryRight(string op, this X, B)(auto ref B b) { return mixin("b "~op~" a"); }
static if (!is(typeof(this) == class))
{
private import std.traits;
static if (isAssignable!(typeof(a)))
{
auto ref opAssign(this X)(auto ref typeof(this) v)
{
a = mixin("v."~a.stringof[5..$]); // remove "this."
return this;
}
}
else
{
@disable void opAssign(this X)(auto ref typeof(this) v);
}
}
auto ref opAssign (this X, V )(auto ref V v) if (!is(V == typeof(this))) { return a = v; }
auto ref opIndexAssign(this X, V, D...)(auto ref V v, auto ref D i) { return a[i] = v; }
auto ref opSliceAssign(this X, V )(auto ref V v) { return a[] = v; }
auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b..e] = v; }
auto ref opOpAssign (string op, this X, V )(auto ref V v) { return mixin("a " ~op~"= v"); }
auto ref opIndexOpAssign(string op, this X, V, D...)(auto ref V v, auto ref D i) { return mixin("a[i] " ~op~"= v"); }
auto ref opSliceOpAssign(string op, this X, V )(auto ref V v) { return mixin("a[] " ~op~"= v"); }
auto ref opSliceOpAssign(string op, this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return mixin("a[b..e] "~op~"= v"); }
template opDispatch(string name)
{
static if (is(typeof(__traits(getMember, a, name)) == function))
{
// non template function
auto ref opDispatch(this X, Args...)(auto ref Args args) { return mixin("a."~name~"(args)"); }
}
else static if (is(typeof({ enum x = mixin("a."~name); })))
{
// built-in type field, manifest constant, and static non-mutable field
enum opDispatch = mixin("a."~name);
}
else static if (is(typeof(mixin("a."~name))) || __traits(getOverloads, a, name).length != 0)
{
// field or property function
@property auto ref opDispatch(this X)() { return mixin("a."~name); }
@property auto ref opDispatch(this X, V)(auto ref V v) { return mixin("a."~name~" = v"); }
}
else
{
// member template
template opDispatch(T...)
{
enum targs = T.length ? "!T" : "";
auto ref opDispatch(this X, Args...)(auto ref Args args){ return mixin("a."~name~targs~"(args)"); }
}
}
}
import std.traits : isArray;
static if (isArray!(typeof(a)))
{
auto opDollar() const { return a.length; }
}
else static if (is(typeof(a.opDollar!0)))
{
auto ref opDollar(size_t pos)() { return a.opDollar!pos(); }
}
else static if (is(typeof(a.opDollar) == function))
{
auto ref opDollar() { return a.opDollar(); }
}
else static if (is(typeof(a.opDollar)))
{
alias opDollar = a.opDollar;
}
}
unittest
{
static struct MyInt
{
private int value;
mixin Proxy!value;
this(int n) inout { value = n; }
enum str = "str";
static immutable arr = [1,2,3];
}
foreach (T; TypeTuple!(MyInt, const MyInt, immutable MyInt))
{
T m = 10;
static assert(!__traits(compiles, { int x = m; }));
static assert(!__traits(compiles, { void func(int n){} func(m); }));
assert(m == 10);
assert(m != 20);
assert(m < 20);
assert(+m == 10);
assert(-m == -10);
assert(cast(double)m == 10.0);
assert(m + 10 == 20);
assert(m - 5 == 5);
assert(m * 20 == 200);
assert(m / 2 == 5);
assert(10 + m == 20);
assert(15 - m == 5);
assert(20 * m == 200);
assert(50 / m == 5);
static if (is(T == MyInt)) // mutable
{
assert(++m == 11);
assert(m++ == 11); assert(m == 12);
assert(--m == 11);
assert(m-- == 11); assert(m == 10);
m = m;
m = 20; assert(m == 20);
}
static assert(T.max == int.max);
static assert(T.min == int.min);
static assert(T.init == int.init);
static assert(T.str == "str");
static assert(T.arr == [1,2,3]);
}
}
unittest
{
static struct MyArray
{
private int[] value;
mixin Proxy!value;
this(int[] arr) { value = arr; }
this(immutable int[] arr) immutable { value = arr; }
}
foreach (T; TypeTuple!(MyArray, const MyArray, immutable MyArray))
{
static if (is(T == immutable) && !is(typeof({ T a = [1,2,3,4]; })))
T a = [1,2,3,4].idup; // workaround until qualified ctor is properly supported
else
T a = [1,2,3,4];
assert(a == [1,2,3,4]);
assert(a != [5,6,7,8]);
assert(+a[0] == 1);
version (LittleEndian)
assert(cast(ulong[])a == [0x0000_0002_0000_0001, 0x0000_0004_0000_0003]);
else
assert(cast(ulong[])a == [0x0000_0001_0000_0002, 0x0000_0003_0000_0004]);
assert(a ~ [10,11] == [1,2,3,4,10,11]);
assert(a[0] == 1);
assert(a[] == [1,2,3,4]);
assert(a[2..4] == [3,4]);
static if (is(T == MyArray)) // mutable
{
a = a;
a = [5,6,7,8]; assert(a == [5,6,7,8]);
a[0] = 0; assert(a == [0,6,7,8]);
a[] = 1; assert(a == [1,1,1,1]);
a[0..3] = 2; assert(a == [2,2,2,1]);
a[0] += 2; assert(a == [4,2,2,1]);
a[] *= 2; assert(a == [8,4,4,2]);
a[0..2] /= 2; assert(a == [4,2,4,2]);
}
}
}
unittest
{
class Foo
{
int field;
@property int val1() const { return field; }
@property void val1(int n) { field = n; }
@property ref int val2() { return field; }
int func(int x, int y) const { return x; }
void func1(ref int a) { a = 9; }
T ifti1(T)(T t) { return t; }
void ifti2(Args...)(Args args) { }
void ifti3(T, Args...)(Args args) { }
T opCast(T)(){ return T.init; }
T tempfunc(T)() { return T.init; }
}
class Hoge
{
Foo foo;
mixin Proxy!foo;
this(Foo f) { foo = f; }
}
auto h = new Hoge(new Foo());
int n;
static assert(!__traits(compiles, { Foo f = h; }));
// field
h.field = 1; // lhs of assign
n = h.field; // rhs of assign
assert(h.field == 1); // lhs of BinExp
assert(1 == h.field); // rhs of BinExp
assert(n == 1);
// getter/setter property function
h.val1 = 4;
n = h.val1;
assert(h.val1 == 4);
assert(4 == h.val1);
assert(n == 4);
// ref getter property function
h.val2 = 8;
n = h.val2;
assert(h.val2 == 8);
assert(8 == h.val2);
assert(n == 8);
// member function
assert(h.func(2,4) == 2);
h.func1(n);
assert(n == 9);
// IFTI
assert(h.ifti1(4) == 4);
h.ifti2(4);
h.ifti3!int(4, 3);
// bug5896 test
assert(h.opCast!int() == 0);
assert(cast(int)h == 0);
const ih = new const Hoge(new Foo());
static assert(!__traits(compiles, ih.opCast!int()));
static assert(!__traits(compiles, cast(int)ih));
// template member function
assert(h.tempfunc!int() == 0);
}
unittest // about Proxy inside a class
{
class MyClass
{
int payload;
mixin Proxy!payload;
this(int i){ payload = i; }
string opCall(string msg){ return msg; }
int pow(int i){ return payload ^^ i; }
}
class MyClass2
{
MyClass payload;
mixin Proxy!payload;
this(int i){ payload = new MyClass(i); }
}
class MyClass3
{
int payload;
mixin Proxy!payload;
this(int i){ payload = i; }
}
// opEquals
Object a = new MyClass(5);
Object b = new MyClass(5);
Object c = new MyClass2(5);
Object d = new MyClass3(5);
assert(a == b);
assert((cast(MyClass)a) == 5);
assert(5 == (cast(MyClass)b));
assert(5 == cast(MyClass2)c);
assert(a != d);
assert(c != a);
// oops! above line is unexpected, isn't it?
// the reason is below.
// MyClass2.opEquals knows MyClass but,
// MyClass.opEquals doesn't know MyClass2.
// so, c.opEquals(a) is true, but a.opEquals(c) is false.
// furthermore, opEquals(T) couldn't be invoked.
assert((cast(MyClass2)c) != (cast(MyClass)a));
// opCmp
Object e = new MyClass2(7);
assert(a < cast(MyClass2)e); // OK. and
assert(e > a); // OK, but...
// assert(a < e); // RUNTIME ERROR!
// assert((cast(MyClass)a) < e); // RUNTIME ERROR!
assert(3 < cast(MyClass)a);
assert((cast(MyClass2)e) < 11);
// opCall
assert((cast(MyClass2)e)("hello") == "hello");
// opCast
assert((cast(MyClass)(cast(MyClass2)c)) == a);
assert((cast(int)(cast(MyClass2)c)) == 5);
// opIndex
class MyClass4
{
string payload;
mixin Proxy!payload;
this(string s){ payload = s; }
}
class MyClass5
{
MyClass4 payload;
mixin Proxy!payload;
this(string s){ payload = new MyClass4(s); }
}
auto f = new MyClass4("hello");
assert(f[1] == 'e');
auto g = new MyClass5("hello");
assert(f[1] == 'e');
// opSlice
assert(f[2..4] == "ll");
// opUnary
assert(-(cast(MyClass2)c) == -5);
// opBinary
assert((cast(MyClass)a) + (cast(MyClass2)c) == 10);
assert(5 + cast(MyClass)a == 10);
// opAssign
(cast(MyClass2)c) = 11;
assert((cast(MyClass2)c) == 11);
(cast(MyClass2)c) = new MyClass(13);
assert((cast(MyClass2)c) == 13);
// opOpAssign
assert((cast(MyClass2)c) += 4);
assert((cast(MyClass2)c) == 17);
// opDispatch
assert((cast(MyClass2)c).pow(2) == 289);
// opDollar
assert(f[2..$-1] == "ll");
// toHash
int[Object] hash;
hash[a] = 19;
hash[c] = 21;
assert(hash[b] == 19);
assert(hash[c] == 21);
}
unittest
{
struct MyInt
{
int payload;
mixin Proxy!payload;
}
MyInt v;
v = v;
struct Foo
{
@disable void opAssign(typeof(this));
}
struct MyFoo
{
Foo payload;
mixin Proxy!payload;
}
MyFoo f;
static assert(!__traits(compiles, f = f));
struct MyFoo2
{
Foo payload;
mixin Proxy!payload;
// override default Proxy behavior
void opAssign(typeof(this) rhs){}
}
MyFoo2 f2;
f2 = f2;
}
unittest
{
// bug8613
static struct Name
{
mixin Proxy!val;
private string val;
this(string s) { val = s; }
}
bool[Name] names;
names[Name("a")] = true;
bool* b = Name("a") in names;
}
/**
$(B Typedef) allows the creation of a unique type which is
based on an existing type. Unlike the $(D alias) feature,
$(B Typedef) ensures the two types are not considered as equals.
Example:
----
alias MyInt = Typedef!int;
static void takeInt(int) { }
static void takeMyInt(MyInt) { }
int i;
takeInt(i); // ok
takeMyInt(i); // fails
MyInt myInt;
takeInt(myInt); // fails
takeMyInt(myInt); // ok
----
Params:
init = Optional initial value for the new type. For example:
----
alias MyInt = Typedef!(int, 10);
MyInt myInt;
assert(myInt == 10); // default-initialized to 10
----
cookie = Optional, used to create multiple unique types which are
based on the same origin type $(D T). For example:
----
alias TypeInt1 = Typedef!int;
alias TypeInt2 = Typedef!int;
// The two Typedefs are the same type.
static assert(is(TypeInt1 == TypeInt2));
alias MoneyEuros = Typedef!(float, float.init, "euros");
alias MoneyDollars = Typedef!(float, float.init, "dollars");
// The two Typedefs are _not_ the same type.
static assert(!is(MoneyEuros == MoneyDollars));
----
Note: If a library routine cannot handle the Typedef type,
you can use the $(D TypedefType) template to extract the
type which the Typedef wraps.
*/
struct Typedef(T, T init = T.init, string cookie=null)
{
private T Typedef_payload = init;
this(T init)
{
Typedef_payload = init;
}
this(Typedef tdef)
{
this(tdef.Typedef_payload);
}
// We need to add special overload for cast(Typedef!X)exp,
// thus we can't simply inherit Proxy!Typedef_payload
T2 opCast(T2 : Typedef!(T, Unused), this X, T, Unused...)()
{
return T2(cast(T)Typedef_payload);
}
auto ref opCast(T2, this X)()
{
return cast(T2)Typedef_payload;
}
mixin Proxy!Typedef_payload;
}
/**
Get the underlying type which a $(D Typedef) wraps.
If $(D T) is not a $(D Typedef) it will alias itself to $(D T).
*/
template TypedefType(T)
{
static if (is(T : Typedef!Arg, Arg))
alias TypedefType = Arg;
else
alias TypedefType = T;
}
///
unittest
{
import std.typecons: Typedef, TypedefType;
import std.conv: to;
alias MyInt = Typedef!int;
static assert(is(TypedefType!MyInt == int));
/// Instantiating with a non-Typedef will return that type
static assert(is(TypedefType!int == int));
string num = "5";
// extract the needed type
MyInt myInt = MyInt( num.to!(TypedefType!MyInt) );
assert(myInt == 5);
// cast to the underlying type to get the value that's being wrapped
int x = cast(TypedefType!MyInt)myInt;
alias MyIntInit = Typedef!(int, 42);
static assert(is(TypedefType!MyIntInit == int));
static assert(MyIntInit() == 42);
}
unittest
{
Typedef!int x = 10;
static assert(!__traits(compiles, { int y = x; }));
static assert(!__traits(compiles, { long z = x; }));
Typedef!int y = 10;
assert(x == y);
static assert(Typedef!int.init == int.init);
Typedef!(float, 1.0) z; // specifies the init
assert(z == 1.0);
static assert(typeof(z).init == 1.0);
alias Dollar = Typedef!(int, 0, "dollar");
alias Yen = Typedef!(int, 0, "yen");
static assert(!is(Dollar == Yen));
Typedef!(int[3]) sa;
static assert(sa.length == 3);
static assert(typeof(sa).length == 3);
Typedef!(int[3]) dollar1;
assert(dollar1[0..$] is dollar1[0..3]);
Typedef!(int[]) dollar2;
dollar2.length = 3;
assert(dollar2[0..$] is dollar2[0..3]);
static struct Dollar1
{
static struct DollarToken {}
enum opDollar = DollarToken.init;
auto opSlice(size_t, DollarToken) { return 1; }
auto opSlice(size_t, size_t) { return 2; }
}
Typedef!Dollar1 drange1;
assert(drange1[0..$] == 1);
assert(drange1[0..1] == 2);
static struct Dollar2
{
size_t opDollar(size_t pos)() { return pos == 0 ? 1 : 100; }
size_t opIndex(size_t i, size_t j) { return i + j; }
}
Typedef!Dollar2 drange2;
assert(drange2[$, $] == 101);
static struct Dollar3
{
size_t opDollar() { return 123; }
size_t opIndex(size_t i) { return i; }
}
Typedef!Dollar3 drange3;
assert(drange3[$] == 123);
}
unittest
{
// bug8655
import std.typecons;
import std.bitmanip;
static import core.stdc.config;
alias c_ulong = Typedef!(core.stdc.config.c_ulong);
static struct Foo
{
mixin(bitfields!(
c_ulong, "NameOffset", 31,
c_ulong, "NameIsString", 1
));
}
}
unittest // Issue 12596
{
import std.typecons;
alias TD = Typedef!int;
TD x = TD(1);
TD y = TD(x);
assert(x == y);
}
unittest // about toHash
{
import std.typecons;
{
alias TD = Typedef!int;
int[TD] td;
td[TD(1)] = 1;
assert(td[TD(1)] == 1);
}
{
alias TD = Typedef!(int[]);
int[TD] td;
td[TD([1,2,3,4])] = 2;
assert(td[TD([1,2,3,4])] == 2);
}
{
alias TD = Typedef!(int[][]);
int[TD] td;
td[TD([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]])] = 3;
assert(td[TD([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]])] == 3);
}
{
struct MyStruct{ int x; }
alias TD = Typedef!MyStruct;
int[TD] td;
td[TD(MyStruct(10))] = 4;
assert(TD(MyStruct(20)) !in td);
assert(td[TD(MyStruct(10))] == 4);
}
{
static struct MyStruct2
{
int x;
size_t toHash() const nothrow @safe { return x; }
bool opEquals(ref const MyStruct2 r) const { return r.x == x; }
}
alias TD = Typedef!MyStruct2;
int[TD] td;
td[TD(MyStruct2(50))] = 5;
assert(td[TD(MyStruct2(50))] == 5);
}
{
class MyClass{}
alias TD = Typedef!MyClass;
int[TD] td;
auto c = new MyClass;
td[TD(c)] = 6;
assert(TD(new MyClass) !in td);
assert(td[TD(c)] == 6);
}
}
unittest
{
alias String = Typedef!(char[]);
alias CString = Typedef!(const(char)[]);
CString cs = "fubar";
String s = cast(String)cs;
assert(cs == s);
char[] s2 = cast(char[])cs;
const(char)[] cs2 = cast(const(char)[])s;
assert(s2 == cs2);
}
/**
Allocates a $(D class) object right inside the current scope,
therefore avoiding the overhead of $(D new). This facility is unsafe;
it is the responsibility of the user to not escape a reference to the
object outside the scope.
Note: it's illegal to move a class reference even if you are sure there
are no pointers to it. As such, it is illegal to move a scoped object.
*/
template scoped(T)
if (is(T == class))
{
// _d_newclass now use default GC alignment (looks like (void*).sizeof * 2 for
// small objects). We will just use the maximum of filed alignments.
alias alignment = classInstanceAlignment!T;
alias aligned = _alignUp!alignment;
static struct Scoped
{
// Addition of `alignment` is required as `Scoped_store` can be misaligned in memory.
private void[aligned(__traits(classInstanceSize, T) + size_t.sizeof) + alignment] Scoped_store = void;
@property inout(T) Scoped_payload() inout
{
void* alignedStore = cast(void*) aligned(cast(size_t) Scoped_store.ptr);
// As `Scoped` can be unaligned moved in memory class instance should be moved accordingly.
immutable size_t d = alignedStore - Scoped_store.ptr;
size_t* currD = cast(size_t*) &Scoped_store[$ - size_t.sizeof];
if(d != *currD)
{
import core.stdc.string;
memmove(alignedStore, Scoped_store.ptr + *currD, __traits(classInstanceSize, T));
*currD = d;
}
return cast(inout(T)) alignedStore;
}
alias Scoped_payload this;
@disable this();
@disable this(this);
~this()
{
// `destroy` will also write .init but we have no functions in druntime
// for deterministic finalization and memory releasing for now.
.destroy(Scoped_payload);
}
}
/// Returns the scoped object
@system auto scoped(Args...)(auto ref Args args)
{
import std.conv : emplace;
Scoped result = void;
void* alignedStore = cast(void*) aligned(cast(size_t) result.Scoped_store.ptr);
immutable size_t d = alignedStore - result.Scoped_store.ptr;
*cast(size_t*) &result.Scoped_store[$ - size_t.sizeof] = d;
emplace!(Unqual!T)(result.Scoped_store[d .. $ - size_t.sizeof], args);
return result;
}
}
///
unittest
{
class A
{
int x;
this() {x = 0;}
this(int i){x = i;}
}
// Standard usage
auto a1 = scoped!A();
auto a2 = scoped!A(1);
a1.x = 42;
assert(a1.x == 42);
assert(a2.x == 1);
// Restrictions
static assert(!is(typeof({
auto e1 = a1; // illegal, scoped objects can't be copied
assert([a2][0].x == 42); // ditto
alias ScopedObject = typeof(a1);
auto e2 = ScopedObject(); //Illegal, must be built via scoped!A
auto e3 = ScopedObject(1); //Illegal, must be built via scoped!A
})));
// Use as member variable
struct B
{
typeof(scoped!A()) a; // note the trailing parentheses
}
// Use with alias
alias makeScopedA = scoped!A;
auto a6 = makeScopedA();
auto a7 = makeScopedA();
}
private size_t _alignUp(size_t alignment)(size_t n)
if(alignment > 0 && !((alignment - 1) & alignment))
{
enum badEnd = alignment - 1; // 0b11, 0b111, ...
return (n + badEnd) & ~badEnd;
}
unittest // Issue 6580 testcase
{
enum alignment = (void*).alignof;
static class C0 { }
static class C1 { byte b; }
static class C2 { byte[2] b; }
static class C3 { byte[3] b; }
static class C7 { byte[7] b; }
static assert(scoped!C0().sizeof % alignment == 0);
static assert(scoped!C1().sizeof % alignment == 0);
static assert(scoped!C2().sizeof % alignment == 0);
static assert(scoped!C3().sizeof % alignment == 0);
static assert(scoped!C7().sizeof % alignment == 0);
enum longAlignment = long.alignof;
static class C1long
{
long long_; byte byte_ = 4;
this() { }
this(long _long, ref int i) { long_ = _long; ++i; }
}
static class C2long { byte[2] byte_ = [5, 6]; long long_ = 7; }
static assert(scoped!C1long().sizeof % longAlignment == 0);
static assert(scoped!C2long().sizeof % longAlignment == 0);
void alignmentTest()
{
int var = 5;
auto c1long = scoped!C1long(3, var);
assert(var == 6);
auto c2long = scoped!C2long();
assert(cast(size_t)&c1long.long_ % longAlignment == 0);
assert(cast(size_t)&c2long.long_ % longAlignment == 0);
assert(c1long.long_ == 3 && c1long.byte_ == 4);
assert(c2long.byte_ == [5, 6] && c2long.long_ == 7);
}
alignmentTest();
version(DigitalMars)
{
void test(size_t size)
{
import core.stdc.stdlib;
alloca(size);
alignmentTest();
}
foreach(i; 0 .. 10)
test(i);
}
else
{
void test(size_t size)()
{
byte[size] arr;
alignmentTest();
}
foreach(i; TypeTuple!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
test!i();
}
}
unittest // Original Issue 6580 testcase
{
class C { int i; byte b; }
auto sa = [scoped!C(), scoped!C()];
assert(cast(size_t)&sa[0].i % int.alignof == 0);
assert(cast(size_t)&sa[1].i % int.alignof == 0); // fails
}
unittest
{
class A { int x = 1; }
auto a1 = scoped!A();
assert(a1.x == 1);
auto a2 = scoped!A();
a1.x = 42;
a2.x = 53;
assert(a1.x == 42);
}
unittest
{
class A { int x = 1; this() { x = 2; } }
auto a1 = scoped!A();
assert(a1.x == 2);
auto a2 = scoped!A();
a1.x = 42;
a2.x = 53;
assert(a1.x == 42);
}
unittest
{
class A { int x = 1; this(int y) { x = y; } ~this() {} }
auto a1 = scoped!A(5);
assert(a1.x == 5);
auto a2 = scoped!A(42);
a1.x = 42;
a2.x = 53;
assert(a1.x == 42);
}
unittest
{
class A { static bool dead; ~this() { dead = true; } }
class B : A { static bool dead; ~this() { dead = true; } }
{
auto b = scoped!B();
}
assert(B.dead, "asdasd");
assert(A.dead, "asdasd");
}
unittest // Issue 8039 testcase
{
static int dels;
static struct S { ~this(){ ++dels; } }
static class A { S s; }
dels = 0; { scoped!A(); }
assert(dels == 1);
static class B { S[2] s; }
dels = 0; { scoped!B(); }
assert(dels == 2);
static struct S2 { S[3] s; }
static class C { S2[2] s; }
dels = 0; { scoped!C(); }
assert(dels == 6);
static class D: A { S2[2] s; }
dels = 0; { scoped!D(); }
assert(dels == 1+6);
}
unittest
{
// bug4500
class A
{
this() { a = this; }
this(int i) { a = this; }
A a;
bool check() { return this is a; }
}
auto a1 = scoped!A();
assert(a1.check());
auto a2 = scoped!A(1);
assert(a2.check());
a1.a = a1;
assert(a1.check());
}
unittest
{
static class A
{
static int sdtor;
this() { ++sdtor; assert(sdtor == 1); }
~this() { assert(sdtor == 1); --sdtor; }
}
interface Bob {}
static class ABob : A, Bob
{
this() { ++sdtor; assert(sdtor == 2); }
~this() { assert(sdtor == 2); --sdtor; }
}
A.sdtor = 0;
scope(exit) assert(A.sdtor == 0);
auto abob = scoped!ABob();
}
unittest
{
static class A { this(int) {} }
static assert(!__traits(compiles, scoped!A()));
}
unittest
{
static class A { @property inout(int) foo() inout { return 1; } }
auto a1 = scoped!A();
assert(a1.foo == 1);
static assert(is(typeof(a1.foo) == int));
auto a2 = scoped!(const(A))();
assert(a2.foo == 1);
static assert(is(typeof(a2.foo) == const(int)));
auto a3 = scoped!(immutable(A))();
assert(a3.foo == 1);
static assert(is(typeof(a3.foo) == immutable(int)));
const c1 = scoped!A();
assert(c1.foo == 1);
static assert(is(typeof(c1.foo) == const(int)));
const c2 = scoped!(const(A))();
assert(c2.foo == 1);
static assert(is(typeof(c2.foo) == const(int)));
const c3 = scoped!(immutable(A))();
assert(c3.foo == 1);
static assert(is(typeof(c3.foo) == immutable(int)));
}
unittest
{
class C { this(ref int val) { assert(val == 3); ++val; } }
int val = 3;
auto s = scoped!C(val);
assert(val == 4);
}
unittest
{
class C
{
this(){}
this(int){}
this(int, int){}
}
alias makeScopedC = scoped!C;
auto a = makeScopedC();
auto b = makeScopedC(1);
auto c = makeScopedC(1, 1);
static assert(is(typeof(a) == typeof(b)));
static assert(is(typeof(b) == typeof(c)));
}
/**
Defines a simple, self-documenting yes/no flag. This makes it easy for
APIs to define functions accepting flags without resorting to $(D
bool), which is opaque in calls, and without needing to define an
enumerated type separately. Using $(D Flag!"Name") instead of $(D
bool) makes the flag's meaning visible in calls. Each yes/no flag has
its own type, which makes confusions and mix-ups impossible.
Example:
Code calling $(D getLine) (usually far away from its definition) can't be
understood without looking at the documentation, even by users familiar with
the API:
----
string getLine(bool keepTerminator)
{
...
if (keepTerminator) ...
...
}
...
auto line = getLine(false);
----
Assuming the reverse meaning (i.e. "ignoreTerminator") and inserting the wrong
code compiles and runs with erroneous results.
After replacing the boolean parameter with an instantiation of $(D Flag), code
calling $(D getLine) can be easily read and understood even by people not
fluent with the API:
----
string getLine(Flag!"keepTerminator" keepTerminator)
{
...
if (keepTerminator) ...
...
}
...
auto line = getLine(Flag!"keepTerminator".yes);
----
Passing categorical data by means of unstructured $(D bool)
parameters is classified under "simple-data coupling" by Steve
McConnell in the $(LUCKY Code Complete) book, along with three other
kinds of coupling. The author argues citing several studies that
coupling has a negative effect on code quality. $(D Flag) offers a
simple structuring method for passing yes/no flags to APIs.
An alias can be used to reduce the verbosity of the flag's type:
----
alias KeepTerminator = Flag!"keepTerminator";
string getline(KeepTerminator keepTerminator)
{
...
if (keepTerminator) ...
...
}
...
// Code calling getLine can now refer to flag values using the shorter name:
auto line = getLine(KeepTerminator.yes);
----
*/
template Flag(string name) {
///
enum Flag : bool
{
/**
When creating a value of type $(D Flag!"Name"), use $(D
Flag!"Name".no) for the negative option. When using a value
of type $(D Flag!"Name"), compare it against $(D
Flag!"Name".no) or just $(D false) or $(D 0). */
no = false,
/** When creating a value of type $(D Flag!"Name"), use $(D
Flag!"Name".yes) for the affirmative option. When using a
value of type $(D Flag!"Name"), compare it against $(D
Flag!"Name".yes).
*/
yes = true
}
}
/**
Convenience names that allow using e.g. $(D Yes.encryption) instead of
$(D Flag!"encryption".yes) and $(D No.encryption) instead of $(D
Flag!"encryption".no).
*/
struct Yes
{
template opDispatch(string name)
{
enum opDispatch = Flag!name.yes;
}
}
//template yes(string name) { enum Flag!name yes = Flag!name.yes; }
/// Ditto
struct No
{
template opDispatch(string name)
{
enum opDispatch = Flag!name.no;
}
}
//template no(string name) { enum Flag!name no = Flag!name.no; }
unittest
{
Flag!"abc" flag1;
assert(flag1 == Flag!"abc".no);
assert(flag1 == No.abc);
assert(!flag1);
if (flag1) assert(false);
flag1 = Yes.abc;
assert(flag1);
if (!flag1) assert(false);
if (flag1) {} else assert(false);
assert(flag1 == Yes.abc);
}
/**
Detect whether an enum is of integral type and has only "flag" values
(i.e. values with a bit count of exactly 1).
Additionally, a zero value is allowed for compatibility with enums including
a "None" value.
*/
template isBitFlagEnum(E)
{
static if (is(E Base == enum) && isIntegral!Base)
{
enum isBitFlagEnum = (E.min >= 0) &&
{
foreach (immutable flag; EnumMembers!E)
{
Base value = flag;
value &= value - 1;
if (value != 0) return false;
}
return true;
}();
}
else
{
enum isBitFlagEnum = false;
}
}
///
@safe pure nothrow unittest
{
enum A
{
None,
A = 1<<0,
B = 1<<1,
C = 1<<2,
D = 1<<3,
}
static assert(isBitFlagEnum!A);
enum B
{
A,
B,
C,
D // D == 3
}
static assert(!isBitFlagEnum!B);
enum C: double
{
A = 1<<0,
B = 1<<1
}
static assert(!isBitFlagEnum!C);
}
/**
A typesafe structure for storing combination of enum values.
This template defines a simple struct to represent bitwise OR combinations of
enum values. It can be used if all the enum values are integral constants with
a bit count of at most 1, or if the $(D unsafe) parameter is explicitly set to
Yes.
This is much safer than using the enum itself to store
the OR combination, which can produce surprising effects like this:
----
enum E
{
A = 1<<0,
B = 1<<1
}
E e = E.A | E.B;
// will throw SwitchError
final switch(e)
{
case E.A:
return;
case E.B:
return;
}
----
*/
struct BitFlags(E, Flag!"unsafe" unsafe = No.unsafe) if (unsafe || isBitFlagEnum!(E))
{
@safe @nogc pure nothrow:
private:
enum isBaseEnumType(T) = is(E == T);
alias Base = OriginalType!E;
Base mValue;
static struct Negation
{
@safe @nogc pure nothrow:
private:
Base mValue;
// Prevent non-copy construction outside the module.
@disable this();
this(Base value)
{
mValue = value;
}
}
public:
this(E flag)
{
this = flag;
}
this(T...)(T flags)
if (allSatisfy!(isBaseEnumType, T))
{
this = flags;
}
bool opCast(B: bool)() const
{
return mValue != 0;
}
Base opCast(B)() const
if (isImplicitlyConvertible!(Base, B))
{
return mValue;
}
Negation opUnary(string op)() const
if (op == "~")
{
return Negation(~mValue);
}
auto ref opAssign(T...)(T flags)
if (allSatisfy!(isBaseEnumType, T))
{
mValue = 0;
foreach (E flag; flags)
{
mValue |= flag;
}
return this;
}
auto ref opAssign(E flag)
{
mValue = flag;
return this;
}
auto ref opOpAssign(string op: "|")(BitFlags flags)
{
mValue |= flags.mValue;
return this;
}
auto ref opOpAssign(string op: "&")(BitFlags flags)
{
mValue &= flags.mValue;
return this;
}
auto ref opOpAssign(string op: "|")(E flag)
{
mValue |= flag;
return this;
}
auto ref opOpAssign(string op: "&")(E flag)
{
mValue &= flag;
return this;
}
auto ref opOpAssign(string op: "&")(Negation negatedFlags)
{
mValue &= negatedFlags.mValue;
return this;
}
auto opBinary(string op)(BitFlags flags) const
if (op == "|" || op == "&")
{
BitFlags result = this;
result.opOpAssign!op(flags);
return result;
}
auto opBinary(string op)(E flag) const
if (op == "|" || op == "&")
{
BitFlags result = this;
result.opOpAssign!op(flag);
return result;
}
auto opBinary(string op: "&")(Negation negatedFlags) const
{
BitFlags result = this;
result.opOpAssign!op(negatedFlags);
return result;
}
auto opBinaryRight(string op)(E flag) const
if (op == "|" || op == "&")
{
return opBinary!op(flag);
}
}
/// BitFlags can be manipulated with the usual operators
@safe @nogc pure nothrow unittest
{
// You can use such an enum with BitFlags straight away
enum Enum
{
None,
A = 1<<0,
B = 1<<1,
C = 1<<2
}
static assert(__traits(compiles, BitFlags!Enum));
// You need to specify the $(D unsafe) parameter for enum with custom values
enum UnsafeEnum
{
A,
B,
C,
D = B|C
}
static assert(!__traits(compiles, BitFlags!UnsafeEnum));
static assert(__traits(compiles, BitFlags!(UnsafeEnum, Yes.unsafe)));
immutable BitFlags!Enum flags_empty;
// A default constructed BitFlags has no value set
assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C));
// Value can be set with the | operator
immutable BitFlags!Enum flags_A = flags_empty | Enum.A;
// And tested with the & operator
assert(flags_A & Enum.A);
// Which commutes
assert(Enum.A & flags_A);
// BitFlags can be variadically initialized
immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B);
assert((flags_AB & Enum.A) && (flags_AB & Enum.B) && !(flags_AB & Enum.C));
// Use the ~ operator for subtracting flags
immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A);
assert(!(flags_B & Enum.A) && (flags_B & Enum.B) && !(flags_B & Enum.C));
// You can use the EnumMembers template to set all flags
immutable BitFlags!Enum flags_all = EnumMembers!Enum;
// use & between BitFlags for intersection
immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C);
assert (flags_B == (flags_BC & flags_AB));
// All the binary operators work in their assignment version
BitFlags!Enum temp = flags_empty;
temp |= flags_AB;
assert(temp == (flags_empty | flags_AB));
temp = flags_empty;
temp |= Enum.B;
assert(temp == (flags_empty | Enum.B));
temp = flags_empty;
temp &= flags_AB;
assert(temp == (flags_empty & flags_AB));
temp = flags_empty;
temp &= Enum.A;
assert(temp == (flags_empty & Enum.A));
// BitFlags with no value set evaluate to false
assert(!flags_empty);
// BitFlags with at least one value set evaluate to true
assert(flags_A);
// This can be useful to check intersection between BitFlags
assert(flags_A & flags_AB);
assert(flags_AB & Enum.A);
// Finally, you can of course get you raw value out of flags
auto value = cast(int)flags_A;
assert(value == Enum.A);
}