std.typecons
Source: std/typecons.d
Synopsis:
// value tuples alias Coord = Tuple!(float, "x", float, "y", float, "z"); Coord c; c[1] = 1; // access by index c.z = 1; // access by given name alias DicEntry = Tuple!(string, string); // names can be omitted // Rebindable references to const and immutable objects void bar() { const w1 = new Widget, w2 = new Widget; w1.foo(); // w1 = w2 would not work; can't rebind const object auto r = Rebindable!(const Widget)(w1); // invoke method as if r were a Widget object r.foo(); // rebind r to refer to another object r = w2; }
- struct
Unique
(T); - Encapsulates unique ownership of a resource. Resource of type T is deleted at the end of the scope, unless it is transferred. The transfer can be explicit, by calling release, or implicit, when returning
Unique
from a function. The resource can be a polymorphic class object, in which caseUnique
behaves polymorphically too.Examples: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);
- alias
RefT
= T; - Represents a reference to T. Resolves to T* if T is a value type.
- Unique!T
create
(A...)(auto ref Aargs
)
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 of this).
Note: Nested structs/classes cannot be created.
Parameters:A args
Arguments to pass to T's constructor. static class C {} auto u = Unique!(C).create();
- this(RefT
p
); - 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(ref RefT
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(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!Uu
)
if (is(u
.RefT : RefT)); - Transfer ownership from a Unique of a type that is convertible to our type.
- const @property bool
isEmpty
(); - Returns whether the resource exists.
- Unique
release
(); - Transfer ownership to a Unique rvalue. Nullifies the current contents.
- struct
Tuple
(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 aTuple
, the individual members are accessible with the syntax obj[0] for the first field, 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. std.meta.AliasSeq iteration), all of which use zero-based indexing.See Also:Parameters:Specs A list of types (and optionally, member names) that the Tuple
contains.Examples:Tuple!(int, int) point; // assign coordinates point[0] = 5; point[1] = 6; // read coordinates 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"; assert(e[1] == "Hello"); assert(e[0] == 4);
Examples:ATuple
with named fields is a distinct type from aTuple
with unnamed fields, i.e. each naming imparts a separate type for theTuple
. 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)));
- alias
Types
= staticMap!(extractType, fieldSpecs); - The types of the Tuple's components.Examples:
alias Fields = Tuple!(int, "id", string, float); static assert(is(Fields.Types == AliasSeq!(int, string, float)));
- alias
fieldNames
= staticMap!(extractName, 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 == AliasSeq!("id", "", ""));
- Types
expand
; - Use t.
expand
for a Tuple t toexpand
it into its components. The result ofexpand
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 ", 2.3); assert(t1.toString() == `Tuple!(int, string, double)(1, " hello ", 2.3)`); void takeSeveralTypes(int n, string s, bool b) { assert(n == 4 && s == "test" && b == false); } auto t2 = tuple(4, "test", false); //t.expand acting as a list of values takeSeveralTypes(t2.expand);
- this(Types
values
); - 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); assert(tup.toString() == `Tuple!(int, string, double)(1, "test", 3.2)`);
- 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)); - Constructor taking a compatible Tuple. Two Tuples are compatible iff they are both of the same length, and, for each type T on the left-hand side, the corresponding type U on the right-hand side can implicitly convert to T.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); //Ok, int can implicitly convert to double DubVec dv = iv; //Error: double cannot implicitly convert to int //IntVec iv2 = dv;
- bool
opEquals
(R)(Rrhs
)
if (areCompatibleTuples!(typeof(this), R, "=="));
const boolopEquals
(R)(Rrhs
)
if (areCompatibleTuples!(typeof(this), R, "==")); - Comparison for equality. Two Tuples are considered equal iff they fulfill the following criteria:
- Each Tuple is the same length.
- For each type T on the left-hand side and each type U on the right-hand side, values of type T can be compared with values of type U.
- For each value v1 on the left-hand side and each value
v2 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, otherwisefalse
.Examples:Tuple!(int, string) t1 = tuple(1, "test"); Tuple!(double, string) t2 = tuple(1.0, "test"); //Ok, int can be compared with double and //both have a value of 1 assert(t1 == t2);
- int
opCmp
(R)(Rrhs
)
if (areCompatibleTuples!(typeof(this), R, "<"));
const intopCmp
(R)(Rrhs
)
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 on the right-hand side and v2 on the left-hand side:- A negative integer if the expression v1 < v2 is
true
. - A positive integer if the expression v1 > v2 is
true
. - 0 if the expression v1 == v2 is
true
.
Examples:The first v1 for which v1 > v2 istrue
determines the result. This could lead to unexpected behaviour.auto tup1 = tuple(1, 1, 1); auto tup2 = tuple(1, 100, 100); assert(tup1 < tup2); //Only the first result matters for comparison tup1[0] = 2; assert(tup1 > tup2);
- A negative integer if the expression v1 < v2 is
- void
opAssign
(R)(auto ref Rrhs
)
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 return auto
rename
(names...)()
if (names.length == 0 || allSatisfy!(isSomeString, typeof(names))); - Renames the elements of a Tuple.
rename
uses the passed names and returns a new Tuple 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"); assert(t0Named.val == 4); assert(t0Named.tag == "hello"); Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!"height"; t1Named.height = 3.4f; assert(t1Named.height == 3.4f); assert(t1Named.pos == [2, 1]); t1Named.rename!"altitude".altitude = 5; assert(t1Named.height == 5); Tuple!(int, "a", int, int, "c") t2; t2 = tuple(3,4,5); auto t2Named = t2.rename!("", "b"); // "a" no longer has a name static assert(!hasMember!(typeof(t2Named), "a")); assert(t2Named[0] == 3); assert(t2Named.b == 4); assert(t2Named.c == 5); // not allowed to specify more names than the tuple has members static assert(!__traits(compiles, t2.rename!("a","b","c","d"))); // use it in a range pipeline import std.range : iota, zip; import std.algorithm.iteration : map, sum; auto res = zip(iota(1, 4), iota(10, 13)) .map!(t => t.rename!("a", "b")) .map!(t => t.a * t.b) .sum; assert(res == 68);
- ref auto
rename
(alias translate)()
if (is(typeof(translate) : V[K], V, K) && isSomeString!V && (isSomeString!K || is(K : size_t))); - Overload of rename that takes an associative array translate as a template parameter, where the keys are either the names or indices of the members to be changed and the new names are the corresponding values. Every key in translate must be the name of a member of the tuple. The same rules for empty strings apply as for the variadic template overload of rename.Examples:
//replacing names by their current name Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!(["dat": "height"]); t1Named.height = 3.4; assert(t1Named.pos == [2, 1]); t1Named.rename!(["height": "altitude"]).altitude = 5; assert(t1Named.height == 5); Tuple!(int, "a", int, "b") t2; t2 = tuple(3, 4); auto t2Named = t2.rename!(["a": "b", "b": "c"]); assert(t2Named.b == 3); assert(t2Named.c == 4);
Examples://replace names by their position Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!([0: "height"]); t1Named.height = 3.4; assert(t1Named.pos == [2, 1]); t1Named.rename!([0: "altitude"]).altitude = 5; assert(t1Named.height == 5); Tuple!(int, "a", int, "b", int, "c") t2; t2 = tuple(3, 4, 5); auto t2Named = t2.rename!([0: "c", 2: "a"]); assert(t2Named.a == 5); assert(t2Named.b == 4); assert(t2Named.c == 3);
- @property ref @trusted Tuple!(sliceSpecs!(from, to))
slice
(size_t from, size_t to)()
if (from <= to && to <= Types.length); - Takes a
slice
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 aslice
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);
- const nothrow @trusted size_t
toHash
(); - Creates a hash of this Tuple.Returns:A size_t representing the hash of this Tuple.
- template
toString
() -
- const string
toString
()(); - Converts to string.Returns:The string representation of this Tuple.
- const void
toString
(DG)(scope DGsink
);
const voidtoString
(DG, Char)(scope DGsink
, FormatSpec!Charfmt
); - 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.
Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ]; // Default format assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`); // One Format for each individual component assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`); assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`); // One Format for all components assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`); // Array of Tuples assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`); // Error: %( %) missing. assertThrown!FormatException( format("%d, %f", tuple(1, 2.0)) == `1, 2.0` ); // Error: %( %| %) missing. assertThrown!FormatException( format("%d", tuple(1, 2)) == `1, 2` ); // Error: %d inadequate for double. assertThrown!FormatException( format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0` );
- auto
reverse
(T)(Tt
)
if (isTuple!T); - Creates a copy of a Tuple with its fields in reverse order.Parameters:
T t
The Tuple to copy. Returns:A new Tuple.Examples:auto tup = tuple(1, "2"); assert(tup.reverse == tuple("2", 1));
- template
tuple
(Names...) - 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. 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. Examples: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");
- auto
tuple
(Args...)(Argsargs
); - 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.
- enum auto
isTuple
(T); - Returns
true
if and only if T is an instance of std.typecons.Tuple.Parameters:T The type to check. 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) 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 useRebindable
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.Parameters:T An object, interface, array slice type, or associative array type. Examples:Regular const object references cannot be reassigned.class Widget { int x; int y() const { return x; } } const a = new Widget; // Fine a.y(); // error! can't modify const a // a.x = 5; // error! can't modify const a // a = new Widget;
Examples:However,Rebindable
!(Widget) does allow reassignment, while otherwise behaving exactly like a const Widget.class Widget { int x; int y() const { return x; } } auto a = Rebindable!(const Widget)(new Widget); // Fine a.y(); // error! can't modify const a // a.x = 5; // Fine a = new Widget;
- Rebindable!T
rebindable
(T)(Tobj
)
if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T); - Convenience function for creating a Rebindable using automatic type inference.Parameters:
T obj
A reference to an object, interface, associative array, or an array slice to initialize the Rebindable with. Returns:A newly constructed Rebindable initialized with the given reference. - Rebindable!T
rebindable
(T)(Rebindable!Tobj
); - This function simply returns the Rebindable object passed in. It's useful in generic programming cases when a given object may be either a regular class or a Rebindable.Parameters:
Rebindable!T obj
An instance of Rebindable!T. Returns:obj
without any modification. - 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);
autonullable
(T)(Tt
); - Defines a value paired with a distinctive "
null
" state that denotes the absence of a value. If default constructed, aNullable
!T object starts in thenull
state. Assigning it renders it non-null
. Calling nullify can nullify it again.PracticallyNullable
!T stores a T and a bool.Examples:struct CustomerRecord { string name; string address; int customerNum; } Nullable!CustomerRecord getByName(string name) { //A bunch of hairy stuff return Nullable!CustomerRecord.init; } auto queryResult = getByName("Doe, John"); if (!queryResult.isNull) { //Process Mr. Doe's customer record auto address = queryResult.address; auto customerNum = queryResult.customerNum; //Do some things with this customer's info } else { //Add the customer to the database }
Examples:import std.exception : assertThrown; auto a = 42.nullable; assert(!a.isNull); assert(a.get == 42); a.nullify(); assert(a.isNull); assertThrown!Throwable(a.get);
- inout this(inout T
value
); - Constructor initializing this with
value
.Parameters:T value
The value
to initialize this Nullable with. - const pure nothrow @property @safe bool
isNull
(); - Check if this is in the
null
state.Returns:true
iff this is in thenull
state, otherwisefalse
.Examples:Nullable!int ni; assert(ni.isNull); ni = 0; assert(!ni.isNull);
- void
nullify
()(); - Forces this to the
null
state.Examples:Nullable!int ni = 0; assert(!ni.isNull); ni.nullify(); assert(ni.isNull);
- void
opAssign
()(Tvalue
); - 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 anull
value
(such as a pointer), then assigning thenull
value
to this Nullable is no different than assigning any othervalue
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.//Passes Nullable!(int*) npi; assert(npi.isNull); //Passes?! npi = null; assert(!npi.isNull);
- inout pure nothrow @property ref @safe inout(T)
get
(); - Gets the value. this must not be in the
null
state. This function is also called for the implicit conversion to T.Returns:The value held internally by this Nullable.Examples:import std.exception : assertThrown, assertNotThrown; Nullable!int ni; //`get` is implicitly called. Will throw //an AssertError in non-release mode assertThrown!Throwable(ni == 0); ni = 0; assertNotThrown!Throwable(ni == 0);
- struct
Nullable
(T, T nullValue);
autonullable
(alias nullValue, T)(Tt
)
if (is(typeof(nullValue) == T)); - Just like
Nullable
!T, except that thenull
state is defined as a particular value. For example,Nullable
!(uint, uint.max) is an uint that sets aside the value uint.max to denote anull
state.Nullable
!(T, nullValue) is more storage-efficient thanNullable
!T because it does not need to store an extra bool.Parameters:T The wrapped type for which Nullable
provides anull
value.nullValue The null
value which denotes thenull
state of thisNullable
. Must be of type T.Examples:Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle) { //Find the needle, returning -1 if not found return Nullable!(size_t, size_t.max).init; } void sendLunchInvite(string name) { } //It's safer than C... auto coworkers = ["Jane", "Jim", "Marry", "Fred"]; auto pos = indexOf(coworkers, "Bob"); if (!pos.isNull) { //Send Bob an invitation to lunch sendLunchInvite(coworkers[pos]); } else { //Bob not found; report the error } //And there's no overhead static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof);
Examples: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);
Examples:auto a = nullable!(int.min)(8); assert(a == 8); a.nullify(); assert(a.isNull);
- this(T
value
); - Constructor initializing this with
value
.Parameters:T value
The value
to initialize this Nullable with. - const @property bool
isNull
(); - Check if this is in the
null
state.Returns:true
iff this is in thenull
state, otherwisefalse
.Examples:Nullable!(int, -1) ni; //Initialized to "null" state assert(ni.isNull); ni = 0; assert(!ni.isNull);
- void
nullify
()(); - Forces this to the
null
state.Examples:Nullable!(int, -1) ni = 0; assert(!ni.isNull); ni = -1; assert(ni.isNull);
- void
opAssign
()(Tvalue
); - Assigns
value
to the internally-held state. If the assignment succeeds, this becomes non-null
. Nonull
checks are made. Note that the assignment may leave this in thenull
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 tonull
.Examples:If this Nullable wraps a type that already has anull
value
(such as a pointer), and thatnull
value
is not given for nullValue, then assigning thenull
value
to this Nullable is no different than assigning any othervalue
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.//Passes enum nullVal = cast(int*)0xCAFEBABE; Nullable!(int*, nullVal) npi; assert(npi.isNull); //Passes?! npi = null; assert(!npi.isNull);
- inout @property ref inout(T)
get
(); - Gets the value. this must not be in the
null
state. This function is also called for the implicit conversion to T.Returns:The value held internally by this Nullable.Examples:import std.exception : assertThrown, assertNotThrown; Nullable!(int, -1) ni; //`get` is implicitly called. Will throw //an error in non-release mode assertThrown!Throwable(ni == 0); ni = 0; assertNotThrown!Throwable(ni == 0);
- struct
NullableRef
(T);
autonullableRef
(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); 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);
- 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 tobind
this NullableRef to.Examples:NullableRef!int nr = new int(42); assert(nr == 42); int* n = new int(1); nr.bind(n); assert(nr == 1);
- const pure nothrow @property @safe bool
isNull
(); - Returns
true
if and only if this is in thenull
state.Returns:true
if this is in thenull
state, otherwisefalse
.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
()(Tvalue
)
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); assert(nr == 42);
- inout pure nothrow @property ref @safe inout(T)
get
(); - 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; //`get` is implicitly called. Will throw //an error in non-release mode assertThrown!Throwable(nr == 0); nr.bind(new int(0)); assertNotThrown!Throwable(nr == 0);
- template
BlackHole
(Base) 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 from Class::BlackHole Perl module by Sean M. Burke.Parameters:Base A non-final class for BlackHole
to inherit from.See Also:Examples:import std.math : isNaN; static abstract class C { int m_value; this(int v) { m_value = v; } int value() @property { return m_value; } abstract real realValue() @property; abstract void doSomething(); } auto c = new BlackHole!C(42); assert(c.value == 42); // Returns real.init which is NaN assert(c.realValue.isNaN); // Abstract functions are implemented as do-nothing c.doSomething();
- template
WhiteHole
(Base) 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 from Class::WhiteHole Perl module by Michael G Schwern.Parameters:Base A non-final class for WhiteHole
to inherit from.See Also:Examples:import std.exception : assertThrown; static class C { abstract void notYetImplemented(); } auto c = new WhiteHole!C; assertThrown!NotImplementedError(c.notYetImplemented()); // throws an Error
- class
AutoImplement
(Base, alias how, alias what = isAbstractFunction): Base; AutoImplement
automatically implements (by default) all abstract member functions in the class or interface Base in specified way.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).
// 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 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.// Sees if fun returns something. enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void);
Note: Generated 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, Bugzilla 3525]
- 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]
- template
generateEmptyFunction
(C, func...)
enum stringgenerateAssertTrap
(C, func...); - Predefined how-policies for AutoImplement. These templates are also used by BlackHole and WhiteHole, respectively.
- template
wrap
(Targets...) if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
templatewrap
(Targets...) if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets)) - Supports structural based typesafe conversion.If Source has structural conformance with the interface Targets,
wrap
creates internal wrapper class which inherits Targets andwrap
src object, then return it. - template
unwrap
(Target) if (isMutable!Target)
templateunwrap
(Target) if (!isMutable!Target) - Extract object which 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(); } // 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);
Examples: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);
- enum
RefCountedAutoInitialize
: int; - Options regarding auto-initialization of a RefCounted object (see the definition of RefCounted below).
no
- Do not auto-initialize the object
yes
- Auto-initialize the object
- struct
RefCounted
(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes) if (!is(T == class) && !is(T == interface)); - Defines a reference-counted object containing a T value as payload.
RefCounted
keeps track of all references of an object, and when the reference count goes down to zero, frees the underlying store.RefCounted
uses malloc and free for operation.RefCounted
is unsafe and should be used with care. No references to the payload should be escaped outside theRefCounted
object. 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 either refCountedStore.isInitialized or refCountedStore.ensureInitialized before attempting to access the payload. Not doing so results innull
pointer dereference.Examples:// 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
RefCountedStore
; - RefCounted storage implementation.
- const pure nothrow @nogc @property @safe bool
isInitialized
(); - Returns
true
if and only if the underlying store has been allocated and initialized. - const pure nothrow @nogc @property @safe size_t
refCount
(); - Returns underlying reference count if it is allocated and initialized (a positive integer), and 0 otherwise.
- void
ensureInitialized
(); - Makes sure the payload was properly initialized. Such a call is typically inserted before using the payload.
- inout nothrow @property ref @safe inout(RefCountedStore)
refCountedStore
(); - Returns storage implementation struct.
- this(A...)(auto ref A
args
)
if (A.length > 0);
this(Tval
); - Constructor that initializes the payload.
Postcondition: refCountedStore.isInitialized
- void
opAssign
(typeof(this)rhs
);
voidopAssign
(Trhs
); - Assignment operators
- @property ref return T
refCountedPayload
();
inout pure nothrow @nogc @property ref return @safe inout(T)refCountedPayload
(); - 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 RefCounted 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 safe and nothrow (but will still assert if not initialized).
- RefCounted!(T, RefCountedAutoInitialize.no)
refCounted
(T)(Tval
); - Initializes a RefCounted with
val
. The template parameter T of RefCounted is inferred fromval
. This function can be used to move non-copyable values to the heap. It also disables the autoInit option of RefCounted.Parameters:T val
The value to be reference counted Returns:An initialized RefCounted containingval
.See Also:Examples:static struct File { string name; @disable this(this); // not copyable ~this() { name = null; } } auto file = File("name"); assert(file.name == "name"); // file cannot be copied and has unique ownership static assert(!__traits(compiles, {auto file2 = file;})); // make the file refcounted to share ownership import std.algorithm.mutation : move; auto rcFile = refCounted(move(file)); assert(rcFile.name == "name"); assert(file.name == null); auto rcFile2 = rcFile; assert(rcFile.refCountedStore.refCount == 2); // file gets properly closed when last reference is dropped
- template
Proxy
(alias a) - 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; // 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);
Examples:The proxied value must be an lvalue.struct NewIntType { //Won't work; the literal '1' is //is an rvalue, not an lvalue //mixin Proxy!1; //Okay, n is an lvalue int n; mixin Proxy!n; this(int n) { this.n = n; } } NewIntType nit = 0; nit++; assert(nit == 1); struct NewObjectType { Object obj; //Ok, obj is an lvalue 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; 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.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
Parameters: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 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 TypedefType template to extract the type which theTypedef
wraps.- template
TypedefType
(T) - Get the underlying type which a Typedef wraps. If T is not a Typedef it will alias itself to T.Examples:
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);
- 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 toscoped
. See below for an example.Note: It'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() {} } // Standard usage, constructing A on the stack auto a1 = scoped!A(); a1.x = 42; // Result of `scoped` call implicitly converts to a class reference A aRef = a1; assert(aRef.x == 42); // Scoped destruction { auto a2 = scoped!A(1); assert(a2.x == 1); aRef = a2; // a2 is destroyed here, calling A's destructor } // aRef is now an invalid reference // Here the temporary scoped A is immediately destroyed. // This means the reference is then invalid. version(Bug) { // Wrong, should use `auto` A invalid = scoped!A(); } // Restrictions version(Bug) { import std.algorithm.mutation : move; auto invalid = a1.move; // illegal, scoped objects can't be moved } static assert(!is(typeof({ auto e1 = a1; // illegal, scoped objects can't be copied assert([a1][0].x == 42); // ditto }))); static assert(!is(typeof({ alias ScopedObject = typeof(a1); auto e2 = ScopedObject(); // illegal, must be built via scoped!A auto e3 = ScopedObject(1); // ditto }))); // Use with alias alias makeScopedA = scoped!A; auto a3 = makeScopedA(); auto a4 = makeScopedA(1); // Use as member variable struct B { typeof(scoped!A()) a; // note the trailing parentheses this(int i) { // construct member a = scoped!A(i); } } // Stack-allocate auto b1 = B(5); aRef = b1.a; assert(aRef.x == 5); destroy(b1); // calls A's destructor for b1.a // aRef is now an invalid reference // Heap-allocate auto b2 = new B(6); assert(b2.a.x == 6); destroy(*b2); // calls A's destructor for b2.a
- @system auto
scoped
(Args...)(auto ref Argsargs
); - 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.Example: Code 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 ofFlag
, 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 andFlag
!"Name".no and are preferred for brevity and readability. These convenience structs mean it is usually unnecessary and counterproductive to create an alias of aFlag
as a way of avoiding typing out the full type while specifying the affirmative or negative options. Passing categorical data by means of unstructured bool parameters 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.- enum
Flag
: bool; -
no
- 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 justfalse
or 0. yes
- 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
.
- struct
Yes
;
structNo
; - Convenience names that allow using e.g.
Yes
.encryption instead of Flag!"encryption".yes andNo
.encryption instead of Flag!"encryption".no.Examples: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);
- 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); 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);
- 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; // will throw SwitchError final switch (e) { case E.A: return; case E.B: return; }
Examples:BitFlags
can be manipulated with the usual operators// You can use such an enum with BitFlags straight away enum Enum { None, A = 1<<0, B = 1<<1, C = 1<<2 } BitFlags!Enum flags1; assert(!(flags1 & (Enum.A | Enum.B | Enum.C))); // 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 flags2; })); BitFlags!(UnsafeEnum, Yes.unsafe) flags3; 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);
- 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]) yields Tuple!(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)) );
- struct
Ternary
; Ternary
type with three truth values:Ternary
.yes for trueTernary
.no for falseTernary
.unknown as an unknown state
See Also:Examples:Ternary a; assert(a == Ternary.unknown); assert(~Ternary.yes == Ternary.no); assert(~Ternary.no == Ternary.yes); assert(~Ternary.unknown == Ternary.unknown);
- enum Ternary
no
;
enum Ternaryyes
;
enum Ternaryunknown
; - The possible states of the Ternary
- pure nothrow @nogc @safe this(bool
b
);
pure nothrow @nogc @safe voidopAssign
(boolb
); - 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 == "~");
TernaryopBinary
(string s)(Ternaryrhs
)
if (s == "|");
TernaryopBinary
(string s)(Ternaryrhs
)
if (s == "&");
TernaryopBinary
(string s)(Ternaryrhs
)
if (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