std.typecons - D Programming Language (original) (raw)
This module implements a variety of type constructors, i.e., templates that allow construction of new, useful general-purpose types.
Encapsulates unique ownership of a resource.
When a Unique
!T goes out of scope it will call destroyon the resource T that it manages, unless it is transferred. One important consequence of destroy is that it will call the destructor of the resource T. GC-managed references are not guaranteed to be valid during a destructor call, but other members ofT, such as file handles or pointers to malloc memory, will still be valid during the destructor call. This allows the resourceT to deallocate or clean up any non-GC resources.
If it is desirable to persist a Unique
!T outside of its original scope, then it can be transferred. The transfer can be explicit, by calling release, or implicit, when returning Unique from a function. The resource T can be a polymorphic class object or instance of an interface, in which case Unique behaves polymorphically too.
If T is a value type, then Unique
!T will be implemented as a reference to a T.
Examples:
struct S { int i; this(int i){this.i = i;} } Unique!S produce() { Unique!S ut = new S(5); return ut; } void increment(ref Unique!S ur) { ur.i++; } void consume(Unique!S u2) { writeln(u2.i); } Unique!S u1; assert(u1.isEmpty); u1 = produce(); writeln(u1.i); increment(u1); writeln(u1.i); consume(u1.release); assert(u1.isEmpty);
Represents a reference to T. Resolves to T* if T is a value type.
Unique!T create
(A...)(auto ref A args
)
if (__traits(compiles, new T(args
)));
Allows safe construction of Unique. It creates the resource and guarantees unique ownership of it (unless T publishes aliases ofthis).
NoteNested structs/classes cannot be created.
Parameters:
A args | Arguments to pass to T's constructor.static class C {} auto u = Unique!(C).create(); |
---|
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;
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(U)(Unique!U u
)
if (is(u
.RefT : RefT));
Constructor that takes a Unique of a type that is convertible to our type.
Typically used to transfer a Unique rvalue of derived type to a Unique of base type.
Example
class C : Object {}
Unique!C uc = new C; Unique!Object uo = uc.release;
void opAssign
(U)(Unique!U u
)
if (is(u
.RefT : RefT));
Transfer ownership from a Unique of a type that is convertible to our type.
@property bool isEmpty
() const;
Returns whether the resource exists.
Transfer ownership to a Unique rvalue. Nullifies the current contents. Same as calling std.algorithm.move on it.
struct Tuple
(Specs...) if (distinctFieldNames!Specs);
Tuple of values, for example Tuple!(int, string) is a record that stores an int and a string. Tuple
can be used to bundle values together, notably when returning multiple values from a function. If obj is a Tuple
, the individual members are accessible with the syntax obj[0] for the first field, obj[1]for the second, and so on.
Parameters:
Specs | A list of types (and optionally, member names) that the Tuple contains. |
---|
Examples:
Tuple!(int, int) point; point[0] = 5; point[1] = 6; auto x = point[0]; auto y = point[1];
Examples:
Tuple
members can be named. It is legal to mix named and unnamed members. The method above is still applicable to all fields.
alias Entry = Tuple!(int, "index", string, "value"); Entry e; e.index = 4; e.value = "Hello"; writeln(e[1]); writeln(e[0]);
Examples:
A Tuple
with named fields is a distinct type from a Tuple
with unnamed fields, i.e. each naming imparts a separate type for the Tuple
. TwoTuple
s differing in naming only are still distinct, even though they might have the same structure.
Tuple!(int, "x", int, "y") point1; Tuple!(int, int) point2; assert(!is(typeof(point1) == typeof(point2)));
Examples:
Use tuples as ranges
import std.algorithm.iteration : sum; import std.range : only; auto t = tuple(1, 2); writeln(t.expand.only.sum);
Examples:
Concatenate tuples
import std.meta : AliasSeq; auto t = tuple(1, "2") ~ tuple(ushort(42), true); static assert(is(t.Types == AliasSeq!(int, string, ushort, bool))); writeln(t[1]); writeln(t[2]); writeln(t[3]);
alias Types
= staticMap!(extractType, fieldSpecs);
The types of the Tuple's components.
alias fieldNames
= staticMap!(extractName, fieldSpecs);
The names of the Tuple's components. Unnamed fields have empty names.
Examples:
import std.meta : AliasSeq; alias Fields = Tuple!(int, "id", string, float); static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
Use t.expand
for a Tuple t to expand it into its components. The result of expand
acts as if the Tuple's components were listed as a list of values. (Ordinarily, a Tuple acts as a single value.)
Examples:
auto t1 = tuple(1, " hello ", 'a'); writeln(t1.toString()); void takeSeveralTypes(int n, string s, bool b) { assert(n == 4 && s == "test" && b == false); }
auto t2 = tuple(4, "test", false); takeSeveralTypes(t2.expand);
Constructor taking one value for each field.
Parameters:
Types values | A list of values that are either the same types as those given by the Types field of this Tuple, or can implicitly convert to those types. They must be in the same order as they appear in Types. |
---|
Examples:
alias ISD = Tuple!(int, string, double); auto tup = ISD(1, "test", 3.2); writeln(tup.toString());
this(U, size_t n)(U[n] values
)
if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types));
Constructor taking a compatible array.
Parameters:
U[n] values | A compatible static array to build the Tuple from. Array slices are not supported. |
---|
Examples:
int[2] ints; Tuple!(int, int) t = ints;
this(U)(U another
)
if (areBuildCompatibleTuples!(typeof(this), U) && (noMemberHasCopyCtor!(typeof(this)) || !is(Unqual!U == Unqual!(typeof(this)))));
Constructor taking a compatible Tuple. Two Tuples are compatibleiff they are both of the same length, and, for each type T on the left-hand side, the corresponding type U on the right-hand side can implicitly convert to T.
Parameters:
U another | A compatible Tuple to build from. Its type must be compatible with the target Tuple's type. |
---|
Examples:
alias IntVec = Tuple!(int, int, int); alias DubVec = Tuple!(double, double, double);
IntVec iv = tuple(1, 1, 1);
DubVec dv = iv;
bool opEquals
(R)(R rhs
)
if (areCompatibleTuples!(typeof(this), R, "=="));
bool opEquals
(R)(R rhs
) const
if (areCompatibleTuples!(typeof(this), R, "=="));
bool opEquals
(R...)(auto ref R rhs
)
if (R.length > 1 && areCompatibleTuples!(typeof(this), Tuple!R, "=="));
Comparison for equality. Two Tuples are considered equaliff they fulfill the following criteria:
- Each Tuple is the same length.
- For each type T on the left-hand side and each typeU on the right-hand side, values of type T can be compared with values of type U.
- For each value v1 on the left-hand side and each valuev2 on the right-hand side, the expression v1 == v2 is true.
Parameters:
R rhs | The Tuple to compare against. It must meeting the criteria for comparison between Tuples. |
---|
Returns:
true if both Tuples are equal, otherwise false.
Examples:
Tuple!(int, string) t1 = tuple(1, "test"); Tuple!(double, string) t2 = tuple(1.0, "test"); writeln(t1);
auto opCmp
(R)(R rhs
)
if (areCompatibleTuples!(typeof(this), R, "<"));
auto opCmp
(R)(R rhs
) const
if (areCompatibleTuples!(typeof(this), R, "<"));
Comparison for ordering.
Parameters:
R rhs | The Tuple to compare against. It must meet the criteria for comparison between Tuples. |
---|
Returns:
For any values v1 contained by the left-hand side tuple and any values v2 contained by the right-hand side:
0 if v1 == v2 for all members or the following value for the first position were the mentioned criteria is not satisfied:
- NaN, in case one of the operands is a NaN.
- A negative number if the expression v1 < v2 is true.
- A positive number if the expression v1 > v2 is true.
Examples:
The first v1 for which v1 > v2 is true determines the result. This could lead to unexpected behaviour.
auto tup1 = tuple(1, 1, 1); auto tup2 = tuple(1, 100, 100); assert(tup1 < tup2);
tup1[0] = 2; assert(tup1 > tup2);
auto opBinary
(string op, T)(auto ref T t
)
if (op == "~" && !(is(T : U[], U) && isTuple!U));
auto opBinaryRight
(string op, T)(auto ref T t
)
if (op == "~" && !(is(T : U[], U) && isTuple!U));
Concatenate Tuples. Tuple concatenation is only allowed if all named fields are distinct (no named field of this tuple occurs in t
and no named field of t
occurs in this tuple).
Parameters:
T t | The Tuple to concatenate with |
---|
Returns:
A concatenation of this tuple and t
ref Tuple opAssign
(R)(auto ref R rhs
)
if (areCompatibleTuples!(typeof(this), R, "="));
Assignment from another Tuple.
Parameters:
R rhs | The source Tuple to assign from. Each element of the source Tuple must be implicitly assignable to each respective element of the target Tuple. |
---|
ref auto rename
(names...)() inout return
if (names.length == 0 || allSatisfy!(isSomeString, typeof(names)));
Renames the elements of a Tuple.
rename
uses the passed names and returns a newTuple using these names, with the content unchanged. If fewer names are passed than there are members of the Tuple then those trailing members are unchanged. An empty string will remove the name for that member. It is an compile-time error to pass more names than there are members of the Tuple.
Examples:
auto t0 = tuple(4, "hello");
auto t0Named = t0.rename!("val", "tag"); writeln(t0Named.val); writeln(t0Named.tag); Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!"height"; t1Named.height = 3.4f; writeln(t1Named.height); writeln(t1Named.pos); t1Named.rename!"altitude".altitude = 5; writeln(t1Named.height); Tuple!(int, "a", int, int, "c") t2; t2 = tuple(3,4,5); auto t2Named = t2.rename!("", "b"); static assert(!__traits(hasMember, typeof(t2Named), "a")); writeln(t2Named[0]); writeln(t2Named.b); writeln(t2Named.c); static assert(!__traits(compiles, t2.rename!("a","b","c","d")));
import std.range : iota, zip; import std.algorithm.iteration : map, sum; auto res = zip(iota(1, 4), iota(10, 13)) .map!(t => t.rename!("a", "b")) .map!(t => t.a * t.b) .sum; writeln(res); const tup = Tuple!(int, "a", int, "b")(2, 3); const renamed = tup.rename!("c", "d"); writeln(renamed.c + renamed.d);
ref auto rename
(alias translate)() inout
if (is(typeof(translate) : V[K], V, K) && isSomeString!V && (isSomeString!K || is(K : size_t)));
Overload of rename that takes an associative arraytranslate as a template parameter, where the keys are either the names or indices of the members to be changed and the new names are the corresponding values. Every key in translate must be the name of a member of thetuple. The same rules for empty strings apply as for the variadic template overload of rename.
Examples:
Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!(["dat": "height"]); t1Named.height = 3.4; writeln(t1Named.pos); t1Named.rename!(["height": "altitude"]).altitude = 5; writeln(t1Named.height); Tuple!(int, "a", int, "b") t2; t2 = tuple(3, 4); auto t2Named = t2.rename!(["a": "b", "b": "c"]); writeln(t2Named.b); writeln(t2Named.c); const t3 = Tuple!(int, "a", int, "b")(3, 4); const t3Named = t3.rename!(["a": "b", "b": "c"]); writeln(t3Named.b); writeln(t3Named.c);
Examples:
Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!([0: "height"]); t1Named.height = 3.4; writeln(t1Named.pos); t1Named.rename!([0: "altitude"]).altitude = 5; writeln(t1Named.height); Tuple!(int, "a", int, "b", int, "c") t2; t2 = tuple(3, 4, 5); auto t2Named = t2.rename!([0: "c", 2: "a"]); writeln(t2Named.a); writeln(t2Named.b); writeln(t2Named.c);
@property ref @trusted inout(Tuple!(sliceSpecs!(from, to))) slice
(size_t from, size_t to)() inout
if (from <= to && (to <= Types.length));
Takes a slice by-reference of this Tuple.
Parameters:
from | A size_t designating the starting position of the slice. |
---|---|
to | A size_t designating the ending position (exclusive) of the slice. |
Returns:
A new Tuple that is a slice from [from, to) of the original. It has the same types and values as the range [from, to) in the original.
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);
Tuple!(int, short, bool, double) b; static assert(!__traits(compiles, b.slice!(2, 4)));
nothrow @safe size_t toHash
() const;
Creates a hash of this Tuple.
Returns:
A size_t representing the hash of this Tuple.
Converts to string.
Returns:
The string representation of this Tuple.
void toString
(DG)(scope DG sink
);
void toString
(DG, Char)(scope DG sink
, ref scope const FormatSpec!Char fmt
);
Formats Tuple with either %s, %(inner%) or %(inner%|sep%).
Formats supported by Tuple
Format | Description |
---|---|
%s | Format like Tuple!(types)(elements formatted with %s each). |
%(inner%) | The format inner is applied the expanded Tuple, so it may contain as many formats as the Tuple has fields. |
%(inner%|sep%) | The format inner is one format, that is applied on all fields of the Tuple. The inner format must be compatible to all of them. |
Examples:
import std.format : format;
Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];
writeln(format("%s", tuple("a", 1))); writeln(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10))); writeln(format("%#x v %.4f w %#x", tuple(1, 1.0, 10).expand)); writeln(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])));
writeln(format("%(%(f(%d) = %.1f%); %)", tupList));
Examples:
import std.exception : assertThrown; import std.format : format, FormatException;
assertThrown!FormatException(
format("%d, %f", tuple(1, 2.0)) == 1, 2.0
);
assertThrown!FormatException(
format("%d", tuple(1, 2)) == 1, 2
);
assertThrown!FormatException(
format("%(%d%|, %)", tuple(1, 2.0)) == 1, 2.0
);
auto reverse
(T)(T t
)
if (isTuple!T);
Creates a copy of a Tuple with its fields in reverse order.
Examples:
auto tup = tuple(1, "2"); writeln(tup.reverse);
Constructs a Tuple object instantiated and initialized according to the given arguments.
Parameters:
Names | An optional list of strings naming each successive field of the Tuple or a list of types that the elements are being casted to. For a list of names, each name matches up with the corresponding field given by Args. A name does not have to be provided for every field, but as the names must proceed in order, it is not possible to skip one field and name the next after it. For a list of types, there must be exactly as many types as parameters. |
---|
Examples:
auto value = tuple(5, 6.7, "hello"); writeln(value[0]); writeln(value[1]); writeln(value[2]); auto entry = tuple!("index", "value")(4, "Hello"); writeln(entry.index); writeln(entry.value);
auto tuple
(Args...)(Args args
);
Parameters:
Args args | Values to initialize the Tuple with. The Tuple's type will be inferred from the types of the values given. |
---|
Returns:
A new Tuple with its type inferred from the arguments given.
Returns true if and only if T is an instance of std.typecons.Tuple.
Returns:
true if T is a Tuple type, false otherwise.
Examples:
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)));
template Rebindable
(T) if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
struct Rebindable
(T) if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T);
Rebindable
!(T) is a simple, efficient wrapper that behaves just like an object of type T, except that you can reassign it to refer to another object. For completeness, Rebindable
!(T) aliases itself away to T if T is a non-const object type.
You may want to use Rebindable
when you want to have mutable storage referring to const objects, for example an array of references that must be sorted in place. Rebindable
does not break the soundness of D's type system and does not incur any of the risks usually associated with cast.
Examples:
Regular const object references cannot be reassigned.
class Widget { int x; int y() @safe const { return x; } } const a = new Widget; a.y();
Examples:
However, Rebindable
!(Widget) does allow reassignment, while otherwise behaving exactly like a const Widget.
class Widget { int x; int y() const @safe { return x; } } auto a = Rebindable!(const Widget)(new Widget); a.y(); a = new Widget;
Examples:
Using Rebindable in a generic algorithm:
import std.range.primitives : front, popFront;
typeof(R.init.front) maxElement(R)(R r) { auto max = rebindable(r.front); r.popFront; foreach (e; r) if (e > max) max = e; return max; } struct S { char[] arr; alias arr this; } const S cs; static assert(!__traits(compiles, { S s = cs; }));
alias CS = const S; CS[] arr = [CS("harp"), CS("apple"), CS("pot")]; CS ms = maxElement(arr); writeln(ms.arr);
Examples:
static struct S { int* ptr; } S s = S(new int);
const cs = s; static assert(!__traits(compiles, {s = cs;}));
Rebindable!(const S) rs = s; assert(rs.ptr is s.ptr); static assert(!__traits(compiles, {rs.ptr = null;}));
static assert(!__traits(compiles, {s = rs;}));
const S cs2 = rs; rs = cs2; rs = S(); assert(rs.ptr is null);
Rebindable!T rebindable
(T)(T obj
)
if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T);
Rebindable!T rebindable
(T)(T value
)
if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T && !is(T : Rebindable!U, U));
Convenience function for creating a Rebindable using automatic type inference.
Parameters:
T obj | A reference to a value to initialize the Rebindable with. |
---|
Returns:
A newly constructed Rebindable initialized with the given reference.
Examples:
class C { int payload; this(int p) { payload = p; } } const c = new C(1);
auto c2 = c.rebindable; writeln(c2.payload); c2 = c2.rebindable;
c2 = new C(2); writeln(c2.payload); const c3 = c2.get; writeln(c3.payload);
Examples:
immutable struct S { int[] array; } auto s1 = [3].idup.rebindable; s1 = [4].idup.rebindable; writeln(s1);
Rebindable!T rebindable
(T)(Rebindable!T obj
);
This function simply returns the Rebindable object passed in. It's useful in generic programming cases when a given object may be either a regularclass or a Rebindable.
Parameters:
Rebindable!T obj | An instance of Rebindable!T. |
---|
Returns:
obj
without any modification.
Examples:
class C { int payload; this(int p) { payload = p; } } const c = new C(1);
auto c2 = c.rebindable; writeln(c2.payload); c2 = c2.rebindable; writeln(c2.payload);
template UnqualRef
(T) if (is(T == class) || is(T == interface))
Similar to Rebindable!(T) but strips all qualifiers from the reference as opposed to just constness / immutability. Primary intended use case is with shared (having thread-local reference to shared class data)
Parameters:
T | A class or interface type. |
---|
Examples:
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);
string alignForSize
(E...)(const char[][] names
...);
Order the provided members to minimize size while preserving alignment. Alignment is not always optimal for 80-bit reals, nor for structs declared as align(1).
Parameters:
E | A list of the types to be aligned, representing fields of an aggregate such as a struct or class. |
---|---|
char[][] names | The names of the fields that are to be aligned. |
Returns:
A string to be mixed in to an aggregate, such as a struct or class.
Examples:
struct Banner { mixin(alignForSize!(byte[6], double)(["name", "height"])); }
struct Nullable
(T);
auto nullable
(T)(T t
);
Defines a value paired with a distinctive "null" state that denotes the absence of a value. If default constructed, a Nullable!T object starts in the null state. Assigning it renders it non-null. Calling nullify can nullify it again.
Practically Nullable
!T stores a T and a bool.
See also:apply, an alternative way to use the payload.
Examples:
struct CustomerRecord { string name; string address; int customerNum; }
Nullable!CustomerRecord getByName(string name) {
return Nullable!CustomerRecord.init;
}
auto queryResult = getByName("Doe, John"); if (!queryResult.isNull) { auto address = queryResult.get.address; auto customerNum = queryResult.get.customerNum;
}
else { }
Examples:
import std.exception : assertThrown;
auto a = 42.nullable; assert(!a.isNull); writeln(a.get); a.nullify(); assert(a.isNull); assertThrown!Throwable(a.get);
Examples:
import std.algorithm.iteration : each, joiner; Nullable!int a = 42; Nullable!int b; int[] arr; a.each!((n) => arr ~= n); writeln(arr); b.each!((n) => arr ~= n); writeln(arr); Nullable!int[] c = new Nullable!int; c[7] = Nullable!int(42); writeln(c.joiner.front);
this(inout T value
) inout;
Constructor initializing this with value
.
Parameters:
T value | The value to initialize this Nullable with. |
---|
bool opEquals
(this This, Rhs)(auto ref Rhs rhs
)
if (!is(CommonType!(This, Rhs) == void));
bool opEquals
(this This, Rhs)(auto ref Rhs rhs
)
if (is(CommonType!(This, Rhs) == void) && is(typeof(this.get == rhs
)));
If they are both null, then they are equal. If one is null and the other is not, then they are not equal. If they are both non-null, then they are equal if their values are equal.
Examples:
Nullable!int empty; Nullable!int a = 42; Nullable!int b = 42; Nullable!int c = 27;
writeln(empty); writeln(empty); assert(empty != a); assert(empty != b); assert(empty != c);
writeln(a); assert(a != c);
assert(empty != 42); writeln(a); assert(c != 42);
string toString
();
string toString
() const;
void toString
(W)(ref W writer
, ref scope const FormatSpec!char fmt
)
if (isOutputRange!(W, char));
void toString
(W)(ref W writer
, ref scope const FormatSpec!char fmt
) const
if (isOutputRange!(W, char));
Gives the string "Nullable.null" if isNull is true. Otherwise, the result is equivalent to calling std.format.formattedWrite on the underlying value.
Returns:
A string if writer
and fmt
are not set; void otherwise.
pure nothrow @property @safe bool isNull
() const;
Check if this is in the null state.
Returns:
true iff this is in the null state, otherwise false.
Examples:
Nullable!int ni; assert(ni.isNull);
ni = 0; assert(!ni.isNull);
bool opCast
(T : bool)() const;
Returns true if this has a value, otherwise false.
Allows a Nullable to be used as the condition in an if statement:
if (auto result = functionReturningNullable()) { doSomethingWith(result.get); }
ref T opCast
(T, this This)()
if (is(This : T) || This.sizeof == T.sizeof);
Prevents opCast
from disabling built-in conversions.
Forces this to the null state.
Examples:
Nullable!int ni = 0; assert(!ni.isNull);
ni.nullify(); assert(ni.isNull);
ref Nullable opAssign
()(T value
) return;
Assigns value
to the internally-held state. If the assignment succeeds, this becomes non-null.
Parameters:
T value | A value of type T to assign to this Nullable. |
---|
Examples:
If this Nullable wraps a type that already has a null value (such as a pointer), then assigning the null value to thisNullable is no different than assigning any other value of type T, and the resulting code will look very strange. It is strongly recommended that this be avoided by instead using the version of Nullable that takes an additional nullValue template argument.
Nullable!(int*) npi; assert(npi.isNull);
npi = null; assert(!npi.isNull);
pure nothrow @property ref @safe inout(T) get
() inout;
@property inout(T) get
()(inout(T) fallback
) inout;
@property auto get
(U)(inout(U) fallback
) inout;
Gets the value if not null. If this is in the null state, and the optional parameter fallback
was provided, it will be returned. Without fallback
, calling get
with a null state is invalid.
When the fallback type is different from the Nullable type, get
(T) returns the common type.
Parameters:
inout(T) fallback | the value to return in case the Nullable is null. |
---|
Returns:
The value held internally by this Nullable.
alias empty
= isNull;
alias popFront
= nullify;
alias popBack
= nullify;
pure nothrow @property ref @safe inout(T) front
() inout;
alias back
= front;
@property inout(typeof(this)) save
() inout;
inout(typeof(this)) opIndex
(size_t[2] dim
) inout;
size_t[2] opSlice
(size_t dim : 0)(size_t from
, size_t to
) const;
pure nothrow @property @safe size_t length
() const;
template opDollar
(size_t dim : 0)
pure nothrow ref @safe inout(T) opIndex
(size_t index
) inout;
auto opSlice
(this This)();
Converts Nullable to a range. Works even when the contained type is immutable.
struct Nullable
(T, T nullValue);
auto nullable
(alias nullValue, T)(T t
)
if (is(typeof(nullValue) == T));
Just like Nullable
!T, except that the null state is defined as a particular value. For example, Nullable!(uint, uint.max) is anuint that sets aside the value uint.max to denote a null state. Nullable!(T, nullValue) is more storage-efficient than Nullable!T because it does not need to store an extra bool.
Parameters:
T | The wrapped type for which Nullable provides a null value. |
---|---|
nullValue | The null value which denotes the null state of thisNullable. Must be of type T. |
Examples:
Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle) {
return Nullable!(size_t, size_t.max).init;
}
void sendLunchInvite(string name) { }
auto coworkers = ["Jane", "Jim", "Marry", "Fred"]; auto pos = indexOf(coworkers, "Bob"); if (!pos.isNull) { sendLunchInvite(coworkers[pos]); } else { }
static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof);
Examples:
import std.exception : assertThrown;
Nullable!(int, int.min) a; assert(a.isNull); assertThrown!Throwable(a.get); a = 5; assert(!a.isNull); writeln(a); static assert(a.sizeof == int.sizeof);
Examples:
auto a = nullable!(int.min)(8); writeln(a); a.nullify(); assert(a.isNull);
Constructor initializing this with value
.
Parameters:
T value | The value to initialize this Nullable with. |
---|
@property bool isNull
() const;
Check if this is in the null state.
Returns:
true iff this is in the null state, otherwise false.
Examples:
Nullable!(int, -1) ni; assert(ni.isNull);
ni = 0; assert(!ni.isNull);
Forces this to the null state.
Examples:
Nullable!(int, -1) ni = 0; assert(!ni.isNull);
ni = -1; assert(ni.isNull);
void opAssign
()(T value
);
Assigns value
to the internally-held state. If the assignment succeeds, this becomes non-null. No null checks are made. Note that the assignment may leave this in the null state.
Parameters:
T value | A value of type T to assign to this Nullable. If it is nullvalue, then the internal state of this Nullable will be set to null. |
---|
Examples:
If this Nullable wraps a type that already has a null value (such as a pointer), and that null value is not given fornullValue, then assigning the null value to this Nullable is no different than assigning any other value of type T, and the resulting code will look very strange. It is strongly recommended that this be avoided by using T's "built in" null value for nullValue.
enum nullVal = cast(int*) 0xCAFEBABE; Nullable!(int*, nullVal) npi; assert(npi.isNull);
npi = null; assert(!npi.isNull);
@property ref inout(T) get
() inout;
Gets the value. this must not be in the null state. This function is also called for the implicit conversion to T.
Preconditions isNull must be false.
Returns:
The value held internally by this Nullable.
Examples:
import std.exception : assertThrown, assertNotThrown;
Nullable!(int, -1) ni; assertThrown!Throwable(ni == 0);
ni = 0; assertNotThrown!Throwable(ni == 0);
template apply
(alias fun)
Unpacks the content of a Nullable, performs an operation and packs it again. Does nothing if isNull.
When called on a Nullable, apply
will unpack the value contained in the Nullable, pass it to the function you provide and wrap the result in another Nullable (if necessary). If the Nullable is null, apply
will return null itself.
Parameters:
T t | a Nullable |
---|---|
fun | a function operating on the content of the nullable |
Returns:
fun(t.get).nullable if !t.isNull, else Nullable.init.
See also:The Maybe monad
Examples:
alias toFloat = i => cast(float) i;
Nullable!int sample;
Nullable!float f = sample.apply!toFloat; assert(sample.isNull && f.isNull);
sample = 3;
f = sample.apply!toFloat; assert(!sample.isNull && !f.isNull); writeln(f.get);
Examples:
alias greaterThree = i => (i > 3) ? i.nullable : Nullable!(typeof(i)).init;
Nullable!int sample;
auto result = sample.apply!greaterThree; assert(sample.isNull && result.isNull);
sample = 3; result = sample.apply!greaterThree; assert(!sample.isNull && result.isNull);
sample = 4; result = sample.apply!greaterThree; assert(!sample.isNull && !result.isNull); writeln(result.get);
struct NullableRef
(T);
auto nullableRef
(T)(T* t
);
Just like Nullable!T, except that the object refers to a value sitting elsewhere in memory. This makes assignments overwrite the initially assigned value. Internally NullableRef
!T only stores a pointer to T (i.e., Nullable!T.sizeof == (T*).sizeof).
Examples:
import std.exception : assertThrown;
int x = 5, y = 7; auto a = nullableRef(&x); assert(!a.isNull); writeln(a); writeln(x); a = 42; writeln(x); assert(!a.isNull); writeln(a); a.nullify(); writeln(x); assert(a.isNull); assertThrown!Throwable(a.get); assertThrown!Throwable(a = 71); a.bind(&y); writeln(a); y = 135; writeln(a);
pure nothrow @safe this(T* value
);
Constructor binding this to value
.
Parameters:
T* value | The value to bind to. |
---|
pure nothrow @safe void bind
(T* value
);
Binds the internal state to value
.
Parameters:
T* value | A pointer to a value of type T to bind this NullableRef to. |
---|
Examples:
NullableRef!int nr = new int(42); writeln(nr); int* n = new int(1); nr.bind(n); writeln(nr);
pure nothrow @property @safe bool isNull
() const;
Returns true if and only if this is in the null state.
Returns:
true if this is in the null state, otherwise false.
Examples:
NullableRef!int nr; assert(nr.isNull);
int* n = new int(42); nr.bind(n); assert(!nr.isNull && nr == 42);
pure nothrow @safe void nullify
();
Forces this to the null state.
Examples:
NullableRef!int nr = new int(42); assert(!nr.isNull);
nr.nullify(); assert(nr.isNull);
void opAssign
()(T value
)
if (isAssignable!T);
Assigns value
to the internally-held state.
Parameters:
T value | A value of type T to assign to this NullableRef. If the internal state of this NullableRef has not been initialized, an error will be thrown in non-release mode. |
---|
Examples:
import std.exception : assertThrown, assertNotThrown;
NullableRef!int nr; assert(nr.isNull); assertThrown!Throwable(nr = 42);
nr.bind(new int(0)); assert(!nr.isNull); assertNotThrown!Throwable(nr = 42); writeln(nr);
pure nothrow @property ref @safe inout(T) get
() inout;
Gets the value. this must not be in the null state. This function is also called for the implicit conversion to T.
Examples:
import std.exception : assertThrown, assertNotThrown;
NullableRef!int nr; assertThrown!Throwable(nr == 0);
nr.bind(new int(0)); assertNotThrown!Throwable(nr == 0);
BlackHole
!Base is a subclass of Base which automatically implements all abstract member functions in Base as do-nothing functions. Each auto-implemented function just returns the default value of the return type without doing anything.
The name came fromClass::BlackHolePerl module by Sean M. Burke.
Parameters:
Base | A non-final class for BlackHole to inherit from. |
---|
Examples:
import std.math.traits : isNaN;
static abstract class C { int m_value; this(int v) { m_value = v; } int value() @property { return m_value; }
abstract real realValue() @property;
abstract void doSomething();
}
auto c = new BlackHole!C(42); writeln(c.value); assert(c.realValue.isNaN); c.doSomething();
template WhiteHole
(Base)
class NotImplementedError
: object.Error;
WhiteHole
!Base is a subclass of Base which automatically implements all abstract member functions as functions that always fail. These functions simply throw an Error and never return. Whitehole is useful for trapping the use of class member functions that haven't been implemented.
The name came fromClass::WhiteHolePerl module by Michael G Schwern.
Parameters:
Base | A non-final class for WhiteHole to inherit from. |
---|
Examples:
import std.exception : assertThrown;
static class C { abstract void notYetImplemented(); }
auto c = new WhiteHole!C; assertThrown!NotImplementedError(c.notYetImplemented());
Examples:
import std.exception : assertThrown; { interface I_1 { void foo(); void bar() nothrow; } auto o = new WhiteHole!I_1; assertThrown!NotImplementedError(o.foo()); assertThrown!NotImplementedError(o.bar()); } { static class C { abstract void notYetImplemented(); }
auto c = new WhiteHole!C;
try
{
c.notYetImplemented();
assert(0);
}
catch (Error e) {}
}
class AutoImplement
(Base, alias how, alias what = isAbstractFunction) if (!is(how == class)): Base;
class AutoImplement
(Interface, BaseClass, alias how, alias what = isAbstractFunction) if (is(Interface == interface) && is(BaseClass == class)): BaseClass, Interface;
AutoImplement
automatically implements (by default) all abstract member functions in the class or interface Base in specified way.
The second version of AutoImplement
automatically implementsInterface, while deriving from BaseClass.
Parameters:
how | template which specifies how functions will be implemented/overridden. Two arguments are passed to how: the type Base and an alias to an implemented function. Then how must return an implemented function body as a string. The generated function body can use these keywords: a0, a1, …: arguments passed to the function; args: a tuple of the arguments; self: an alias to the function itself; 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: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 what: an alias to a non-final member function in Base. Then what must return a boolean value. Return true to indicate that the passed function should be implemented/overridden.enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void); |
NoteGenerated code is inserted in the scope of std.typecons module. Thus, any useful functions outside std.typecons cannot be used in the generated code. To workaround this problem, you may import necessary things in a local struct, as done in the generateLogger() template in the above example.
Bugs:
- Variadic arguments to constructors are not forwarded to super.
- Deep interface inheritance causes compile error with messages like "Error: function std.typecons.AutoImplement!(Foo).AutoImplement.bar does not override any function". [Bugzilla 2525]
- The parent keyword is actually a delegate to the super class' corresponding member function. [Bugzilla 2540]
- Using alias template parameter in how and/or what may cause strange compile error. Use template tuple parameter instead to workaround this problem. [Bugzilla 4217]
Examples:
interface PackageSupplier { int foo(); int bar(); }
static abstract class AbstractFallbackPackageSupplier : PackageSupplier { protected PackageSupplier default_, fallback;
this(PackageSupplier default_, PackageSupplier fallback)
{
this.default_ = default_;
this.fallback = fallback;
}
abstract int foo();
abstract int bar();
}
template fallback(T, alias func) { import std.format : format; enum fallback = q{ try { return default_.%1$s(args); } catch (Exception) { return fallback.%1$s(args); } }.format(__traits(identifier, func)); }
alias FallbackPackageSupplier = AutoImplement!(AbstractFallbackPackageSupplier, fallback);
class FailingPackageSupplier : PackageSupplier { int foo(){ throw new Exception("failure"); } int bar(){ return 2;} }
class BackupPackageSupplier : PackageSupplier { int foo(){ return -1; } int bar(){ return -1;} }
auto registry = new FallbackPackageSupplier(new FailingPackageSupplier(), new BackupPackageSupplier());
writeln(registry.foo()); writeln(registry.bar());
template generateEmptyFunction
(C, func...)
enum string generateAssertTrap
(C, func...);
Predefined how-policies for AutoImplement. These templates are also used byBlackHole and WhiteHole, respectively.
Examples:
alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction);
interface I { int foo(); string bar(); }
auto i = new BlackHole!I(); writeln(i.foo); assert(i.bar is null);
Examples:
import std.exception : assertThrown;
alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap);
interface I { int foo(); string bar(); }
auto i = new WhiteHole!I(); assertThrown!NotImplementedError(i.foo); assertThrown!NotImplementedError(i.bar);
template wrap
(Targets...) if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
template wrap
(Targets...) if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))
template unwrap
(Target) if (isMutable!Target)
template unwrap
(Target) if (!isMutable!Target)
Supports structural based typesafe conversion.
If Source has structural conformance with the interface Targets, wrap creates an internal wrapper class which inherits Targets and wraps the src object, then returns it.
unwrap
can be used to extract objects which have been wrapped by wrap
.
Examples:
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(); }
static assert(!__traits(compiles, d1.wrap!Refleshable)); static assert(!__traits(compiles, h1.wrap!Refleshable));
Quack qd = d1.wrap!Quack; assert(qd is d1); assert(qd.quack() == 1); Duck d2 = qd.unwrap!Duck; assert(d2 is d1);
Quack qh = h1.wrap!Quack; assert(qh.quack() == 2); Human h2 = qh.unwrap!Human; assert(h2 is h1);
Quack qx = h1.wrap!Quack; Flyer fx = qx.wrap!Flyer; assert(fx.height == 20); Quack qy = fx.unwrap!Quack; Human hy = qy.unwrap!Human; assert(hy is h1); Human hz = fx.unwrap!Human; assert(hz is h1);
Examples:
import std.traits : FunctionAttribute, functionAttributes; 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; writeln(a.run()); writeln(b.stop()); writeln(b.status); static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
enum RefCountedAutoInitialize
: int;
Options regarding auto-initialization of a SafeRefCounted object (see the definition of SafeRefCounted below).
Examples:
import core.exception : AssertError; import std.exception : assertThrown;
struct Foo { int a = 42; }
SafeRefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto; SafeRefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto;
writeln(rcAuto.refCountedPayload.a); assertThrown!AssertError(rcNoAuto.refCountedPayload); rcNoAuto.refCountedStore.ensureInitialized; writeln(rcNoAuto.refCountedPayload.a);
Do not auto-initialize the object
Auto-initialize the object
struct SafeRefCounted
(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes) if (!is(T == class) && !is(T == interface));
Defines a reference-counted object containing a T value as payload.
An instance of SafeRefCounted
is a reference to a structure, which is referred to as the store, or storage implementation struct in this documentation. The store contains a reference count and the T payload. SafeRefCounted
uses malloc to allocate the store. As instances of SafeRefCounted
are copied or go out of scope, they will automatically increment or decrement the reference count. When the reference count goes down to zero, SafeRefCounted
will call destroy against the payload and call free to deallocate the store. If the T payload contains any references to GC-allocated memory, then SafeRefCounted
will add it to the GC memory that is scanned for pointers, and remove it from GC scanning beforefree is called on the store.
One important consequence of destroy is that it will call the destructor of the T payload. GC-managed references are not guaranteed to be valid during a destructor call, but other members ofT, such as file handles or pointers to malloc memory, will still be valid during the destructor call. This allows the T to deallocate or clean up any non-GC resources immediately after the reference count has reached zero.
Without -preview=dip1000, SafeRefCounted
is unsafe and should be used with care. No references to the payload should be escaped outside the SafeRefCounted
object.
With -preview=dip1000, SafeRefCounted
is safe if it's payload is accessed only with the borrow function. Scope semantics can also prevent accidental escaping of refCountedPayload, but it's still up to the user to not destroy the last counted reference while the payload is in use. Due to that,refCountedPayload remains accessible only in @system code.
The autoInit option makes the object ensure the store is automatically initialized. Leaving autoInit == RefCountedAutoInitialize.yes (the default option) is convenient but has the cost of a test whenever the payload is accessed. If autoInit == RefCountedAutoInitialize.no, user code must call eitherrefCountedStore.isInitialized or refCountedStore.ensureInitializedbefore attempting to access the payload. Not doing so results in null pointer dereference.
If T.this() is annotated with @disable then autoInit must beRefCountedAutoInitialize.no in order to compile.
Examples:
auto rc1 = SafeRefCounted!int(5); writeln(rc1); auto rc2 = rc1; rc2 = 42; writeln(rc1);
SafeRefCounted storage implementation.
pure nothrow @nogc @property @safe bool isInitialized
() const;
Returns true if and only if the underlying store has been allocated and initialized.
pure nothrow @nogc @property @safe size_t refCount
() const;
Returns underlying reference count if it is allocated and initialized (a positive integer), and 0 otherwise.
pure nothrow @safe void ensureInitialized
()();
Makes sure the payload was properly initialized. Such a call is typically inserted before using the payload.
This function is unavailable if T.this() is annotated with@disable.
nothrow @property ref @safe inout(RefCountedStore) refCountedStore
() inout;
Returns storage implementation struct.
this(A...)(auto ref A args
)
if (A.length > 0);
this(return scope T val
);
Constructor that initializes the payload.
Postcondition refCountedStore.isInitialized
void opAssign
(typeof(this) rhs
);
void opAssign
(T rhs
);
Assignment operators.
NoteYou may not assign a new payload to an uninitialized SafeRefCounted, if auto initialization is off. Assigning another counted reference is still okay.
@property ref @system T refCountedPayload
() return;
pure nothrow @nogc @property ref @system inout(T) refCountedPayload
() inout return;
Returns a reference to the payload. If (autoInit == RefCountedAutoInitialize.yes), calls refCountedStore.ensureInitialized. Otherwise, just issues assert(refCountedStore.isInitialized). Used with alias refCountedPayload this;, so callers can just use the SafeRefCounted object as a T.
The first overload exists only if autoInit == RefCountedAutoInitialize.yes. So if autoInit == RefCountedAutoInitialize.no or called for a constant or immutable object, thenrefCountedPayload
will also be qualified as nothrow (but will still assert if not initialized).
template borrow
(alias fun)
Borrows the payload of SafeRefCounted for use in fun. Inferred as @safeif fun is @safe and does not escape a reference to the payload. The reference count will be incremented for the duration of the operation, so destroying the last reference will not leave dangling references infun.
Parameters:
fun | A callable accepting the payload either by value or by reference. |
---|---|
RC refCount | The counted reference to the payload. |
Returns:
The return value of fun, if any. ref in the return value will be forwarded.
IssuesFor yet unknown reason, code that uses this function with UFCS syntax will not be inferred as @safe. It will still compile if the code is explicitly marked @safe and nothing in fun prevents that.
Examples:
This example can be marked @safe with -preview=dip1000.
auto rcInt = safeRefCounted(5); writeln(rcInt.borrow!(theInt => theInt)); auto sameInt = rcInt; writeln(sameInt.borrow!"a"); auto arr = [0, 1, 2, 3, 4, 5, 6]; sameInt.borrow!(ref (x) => arr[x]) = 10; writeln(arr); sameInt.borrow!"a*=2"; writeln(rcInt.borrow!"a");
SafeRefCounted!(T, RefCountedAutoInitialize.no) safeRefCounted
(T)(T val
);
Initializes a SafeRefCounted with val
. The template parameterT of SafeRefCounted is inferred from val
. This function can be used to move non-copyable values to the heap. It also disables the autoInit option of SafeRefCounted.
Parameters:
T val | The value to be reference counted |
---|
Returns:
An initialized SafeRefCounted containing val
.
Examples:
static struct File { static size_t nDestroyed; string name; @disable this(this); ~this() { name = null; ++nDestroyed; } }
auto file = File("name"); writeln(file.name); static assert(!__traits(compiles, {auto file2 = file;}));
writeln(File.nDestroyed); { import std.algorithm.mutation : move; auto rcFile = safeRefCounted(move(file)); writeln(rcFile.name); writeln(File.nDestroyed); writeln(file.name); auto rcFile2 = rcFile; writeln(rcFile.refCountedStore.refCount); writeln(File.nDestroyed); } writeln(File.nDestroyed);
Creates a proxy for the value a that will forward all operations while disabling implicit conversions. The aliased item a must be an lvalue. This is useful for creating a new type from the "base" type (though this is not a subtype-supertype relationship; the new type is not related to the old type in any way, by design).
The new type supports all operations that the underlying type does, including all operators such as +, --, <, [], etc.
Parameters:
a | The value to act as a proxy for all operations. It must be an lvalue. |
---|
Examples:
struct MyInt { private int value; mixin Proxy!value;
this(int n){ value = n; }
}
MyInt n = 10;
++n; writeln(n); writeln(n * 2); void func(int n) { }
Examples:
The proxied value must be an lvalue.
struct NewIntType {
int n;
mixin Proxy!n;
this(int n) { this.n = n; }
}
NewIntType nit = 0; nit++; writeln(nit);
struct NewObjectType { Object obj; mixin Proxy!obj;
this (Object o) { obj = o; }
}
NewObjectType not = new Object(); assert(__traits(compiles, not.toHash()));
Examples:
There is one exception to the fact that the new type is not related to the old type. Pseudo-member functions are usable with the new type; they will be forwarded on to the proxied value.
import std.math.traits : isInfinity;
float f = 1.0; assert(!f.isInfinity);
struct NewFloat { float ; mixin Proxy!;
this(float f) { _ = f; }
}
NewFloat nf = 1.0f; assert(!nf.isInfinity);
struct Typedef
(T, T init = T.init, string cookie = null);
Typedef allows the creation of a unique type which is based on an existing type. Unlike the alias feature,Typedef ensures the two types are not considered as equals.
Parameters:
init | Optional initial value for the new type. |
---|---|
cookie | Optional, used to create multiple unique types which are based on the same origin type T |
NoteIf a library routine cannot handle the Typedef type, you can use the TypedefType template to extract the type which the Typedef wraps.
Examples:
alias MyInt = Typedef!int; MyInt foo = 10; foo++; writeln(foo);
Examples:
custom initialization values
alias MyIntInit = Typedef!(int, 42); static assert(is(TypedefType!MyIntInit == int)); static assert(MyIntInit() == 42);
Examples:
Typedef creates a new type
alias MyInt = Typedef!int; static void takeInt(int) {} static void takeMyInt(MyInt) {}
int i; takeInt(i); static assert(!__traits(compiles, takeMyInt(i)));
MyInt myInt; static assert(!__traits(compiles, takeInt(myInt))); takeMyInt(myInt);
Examples:
Use the optional cookie argument to create different types of the same base type
alias TypeInt1 = Typedef!int; alias TypeInt2 = Typedef!int;
static assert(is(TypeInt1 == TypeInt2));
alias MoneyEuros = Typedef!(float, float.init, "euros"); alias MoneyDollars = Typedef!(float, float.init, "dollars");
static assert(!is(MoneyEuros == MoneyDollars));
string toString
(this T)();
void toString
(this T, W)(ref W writer
, ref scope const FormatSpec!char fmt
)
if (isOutputRange!(W, char));
Convert wrapped value to a human readable string
Examples:
import std.conv : to;
int i = 123; auto td = Typedef!int(i); writeln(i.to!string);
Get the underlying type which a Typedef wraps. If T is not a Typedef it will alias itself to T.
Examples:
import std.conv : to;
alias MyInt = Typedef!int; static assert(is(TypedefType!MyInt == int));
static assert(is(TypedefType!int == int));
string num = "5";
MyInt myInt = MyInt( num.to!(TypedefType!MyInt) ); writeln(myInt); int x = cast(TypedefType!MyInt) myInt;
alias MyIntInit = Typedef!(int, 42); static assert(is(TypedefType!MyIntInit == int)); static assert(MyIntInit() == 42);
template scoped
(T) if (is(T == class))
Allocates a class object right inside the current scope, therefore avoiding the overhead of new. This facility is unsafe; it is the responsibility of the user to not escape a reference to the object outside the scope.
The class destructor will be called when the result of scoped
() is itself destroyed.
Scoped class instances can be embedded in a parent class or struct, just like a child struct instance. Scoped member variables must have type typeof(scoped
!Class(args)), and be initialized with a call to scoped. See below for an example.
NoteIt's illegal to move a class instance even if you are sure there are no pointers to it. As such, it is illegal to move a scoped object.
Examples:
class A { int x; this() {x = 0;} this(int i){x = i;} ~this() {} }
auto a1 = scoped!A(); a1.x = 42;
A aRef = a1; writeln(aRef.x); { auto a2 = scoped!A(1); writeln(a2.x); aRef = a2; }
version (Bug) { A invalid = scoped!A(); }
version (Bug) { import std.algorithm.mutation : move; auto invalid = a1.move; } static assert(!is(typeof({ auto e1 = a1; assert([a1][0].x == 42); }))); static assert(!is(typeof({ alias ScopedObject = typeof(a1); auto e2 = ScopedObject(); auto e3 = ScopedObject(1); })));
alias makeScopedA = scoped!A; auto a3 = makeScopedA(); auto a4 = makeScopedA(1);
struct B { typeof(scoped!A()) a; this(int i) { a = scoped!A(i); } }
auto b1 = B(5); aRef = b1.a; writeln(aRef.x); destroy(b1); auto b2 = new B(6); writeln(b2.a.x); destroy(*b2);
@system auto scoped
(Args...)(auto ref Args args
);
Returns the scoped object.
Parameters:
Args args | Arguments to pass to T's constructor. |
---|
template Flag
(string name)
Defines a simple, self-documenting yes/no flag. This makes it easy for APIs to define functions accepting flags without resorting to bool, which is opaque in calls, and without needing to define an enumerated type separately. Using Flag
!"Name" instead of bool makes the flag's meaning visible in calls. Each yes/no flag has its own type, which makes confusions and mix-ups impossible.
ExampleCode calling 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 Flag
, code calling 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(Yes.keepTerminator);
The structs Yes and No are provided as shorthand forFlag
!"Name".yes and Flag
!"Name".no and are preferred for brevity and readability. These convenience structs mean it is usually unnecessary and counterproductive to create an alias of a Flag
as a way of avoiding typing out the full type while specifying the affirmative or negative options.
Passing categorical data by means of unstructured boolparameters is classified under "simple-data coupling" by Steve McConnell in the 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. Flag
offers a simple structuring method for passing yes/no flags to APIs.
Examples:
Flag!"abc" flag;
writeln(flag); writeln(flag); assert(!flag); if (flag) assert(0);
Examples:
auto flag = Yes.abc;
assert(flag); writeln(flag); if (!flag) assert(0); if (flag) {} else assert(0);
When creating a value of type Flag!"Name", use Flag!"Name".no for the negative option. When using a value of type Flag!"Name", compare it against Flag!"Name".no or just false or 0.
When creating a value of type Flag!"Name", use Flag!"Name".yes for the affirmative option. When using a value of type Flag!"Name", compare it against Flag!"Name".yes.
Convenience names that allow using e.g. Yes
.encryption instead ofFlag!"encryption".yes and No
.encryption instead of Flag!"encryption".no.
Examples:
Flag!"abc" flag;
writeln(flag); writeln(flag); assert(!flag); if (flag) assert(0);
Examples:
auto flag = Yes.abc;
assert(flag); writeln(flag); if (!flag) assert(0); if (flag) {} else assert(0);
enum auto opDispatch
(string name);
template isBitFlagEnum
(E)
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.
Examples:
enum A { None, A = 1 << 0, B = 1 << 1, C = 1 << 2, D = 1 << 3, }
static assert(isBitFlagEnum!A);
Examples:
Test an enum with default (consecutive) values
enum B { A, B, C, D }
static assert(!isBitFlagEnum!B);
Examples:
Test an enum with non-integral values
enum C: double { A = 1 << 0, B = 1 << 1 }
static assert(!isBitFlagEnum!C);
struct BitFlags
(E, Flag!"unsafe" unsafe = No.unsafe) if (unsafe || isBitFlagEnum!E);
A typesafe structure for storing combinations 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 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; final switch (e) { case E.A: return; case E.B: return; }
Examples:
Set values with the | operator and test with &
enum Enum { A = 1 << 0, }
immutable BitFlags!Enum flags_empty; assert(!flags_empty.A);
immutable flags_A = flags_empty | Enum.A;
assert(flags_A.A);
assert(flags_A & Enum.A); assert(Enum.A & flags_A);
Examples:
A default constructed BitFlags has no value set
enum Enum { None, A = 1 << 0, B = 1 << 1, C = 1 << 2 }
immutable BitFlags!Enum flags_empty; assert(!(flags_empty & (Enum.A | Enum.B | Enum.C))); assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C));
Examples:
Binary operations: subtracting and intersecting flags
enum Enum { A = 1 << 0, B = 1 << 1, C = 1 << 2, } immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B); immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C);
immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A); assert(!flags_B.A && flags_B.B && !flags_B.C);
writeln(flags_B);
Examples:
All the binary operators work in their assignment version
enum Enum { A = 1 << 0, B = 1 << 1, }
BitFlags!Enum flags_empty, temp, flags_AB; flags_AB = Enum.A | Enum.B;
temp |= flags_AB; writeln(temp); temp = flags_empty; temp |= Enum.B; writeln(temp); temp = flags_empty; temp &= flags_AB; writeln(temp); temp = flags_empty; temp &= Enum.A; writeln(temp);
Examples:
Conversion to bool and int
enum Enum { A = 1 << 0, B = 1 << 1, }
BitFlags!Enum flags;
assert(!flags);
flags |= Enum.A; assert(flags);
BitFlags!Enum flags_AB = Enum.A | Enum.B; assert(flags & flags_AB); assert(flags & Enum.A);
auto value = cast(int) flags; writeln(value);
Examples:
You need to specify the unsafe parameter for enums with custom values
enum UnsafeEnum { A = 1, B = 2, C = 4, BC = B|C } static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags; })); BitFlags!(UnsafeEnum, Yes.unsafe) flags;
flags.B = true; assert(!flags.BC); flags.C = true; assert(flags.BC); flags.B = false; assert(!flags.BC); flags = flags.init; flags.BC = true; assert(!flags.A && flags.B && flags.C); flags.A = true; flags.BC = false; assert(flags.A && !flags.B && !flags.C);
template ReplaceType
(From, To, T...)
Replaces all occurrences of From into To, in one or more types T. For example, ReplaceType
!(int, uint, Tuple!(int, float)[string]) yieldsTuple!(uint, float)[string]. The types in which replacement is performed may be arbitrarily complex, including qualifiers, built-in type constructors (pointers, arrays, associative arrays, functions, and delegates), and template instantiations; replacement proceeds transitively through the type definition. However, member types in structs or classes are not replaced because there are no ways to express the types resulting after replacement.
This is an advanced type manipulation necessary e.g. for replacing the placeholder type This in std.variant.Algebraic.
Returns:
ReplaceType
aliases itself to the type(s) that result after replacement.
Examples:
static assert( is(ReplaceType!(int, string, int[]) == string[]) && is(ReplaceType!(int, string, int[int]) == string[string]) && is(ReplaceType!(int, string, const(int)[]) == const(string)[]) && is(ReplaceType!(int, string, Tuple!(int[], float)) == Tuple!(string[], float)) );
template ReplaceTypeUnless
(alias pred, From, To, T...)
Like ReplaceType, but does not perform replacement in types for whichpred evaluates to true.
Examples:
import std.traits : isArray;
static assert( is(ReplaceTypeUnless!(isArray, int, string, int*) == string*) && is(ReplaceTypeUnless!(isArray, int, string, int[]) == int[]) && is(ReplaceTypeUnless!(isArray, int, string, Tuple!(int, int[])) == Tuple!(string, int[])) );
Ternary type with three truth values:
Ternary
.yes for trueTernary
.no for falseTernary
.unknown as an unknown state
Also known as trinary, trivalent, or trilean.
Examples:
Ternary a;
writeln(a);
writeln(Ternary.yes); writeln(Ternary.no); writeln(~Ternary.unknown);
enum Ternary no
;
enum Ternary yes
;
enum Ternary unknown
;
The possible states of the Ternary
pure nothrow @nogc @safe this(bool b
);
pure nothrow @nogc @safe void opAssign
(bool b
);
Construct and assign from a bool, receiving no for false and yes for true.
pure nothrow @nogc @safe this(const Ternary b
);
Construct a ternary value from another ternary value
Ternary opUnary
(string s)()
if (s == "~");
Ternary opBinary
(string s)(Ternary rhs
)
if (s == "|");
Ternary opBinary
(string s)(Ternary rhs
)
if (s == "&");
Ternary opBinary
(string s)(Ternary rhs
)
if (s == "^");
Ternary opBinary
(string s)(bool rhs
)
if (s == "|" || s == "&" || s == "^");
Truth table for logical operations
a | b | ˜a | a | b | a & b | a ^ b |
---|---|---|---|---|---|
no | no | yes | no | no | no |
no | yes | yes | no | yes | |
no | unknown | unknown | no | unknown | |
yes | no | no | yes | no | yes |
yes | yes | yes | yes | no | |
yes | unknown | yes | unknown | unknown | |
unknown | no | unknown | unknown | no | unknown |
unknown | yes | yes | unknown | unknown | |
unknown | unknown | unknown | unknown | unknown |
struct RefCounted
(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes);
The old version of SafeRefCounted, before borrow existed. Old code may be relying on @safety of some of the member functions which cannot be safe in the new scheme, and can avoid breakage by continuing to use this. SafeRefCounted should be preferred, as this type is outdated and unrecommended for new code.
Examples:
auto rc1 = RefCounted!int(5); writeln(rc1); auto rc2 = rc1; rc2 = 42; writeln(rc1);
RefCounted!(T, RefCountedAutoInitialize.no) refCounted
(T)(T val
);
Like safeRefCounted but used to initialize RefCounted instead. Intended for backwards compatibility, otherwise it is preferable to use safeRefCounted.
Examples:
static struct File { static size_t nDestroyed; string name; @disable this(this); ~this() { name = null; ++nDestroyed; } }
auto file = File("name"); writeln(file.name); static assert(!__traits(compiles, {auto file2 = file;})); writeln(File.nDestroyed); { import std.algorithm.mutation : move; auto rcFile = refCounted(move(file)); writeln(rcFile.name); writeln(File.nDestroyed); writeln(file.name); auto rcFile2 = rcFile; writeln(rcFile.refCountedStore.refCount); writeln(File.nDestroyed); }
writeln(File.nDestroyed);
Copyright © 1999-2025 by the D Language Foundation | Page generated byDdoc on Sun May 4 23:59:37 2025