std.typecons
This module implements a variety of type constructors, i.e., templates that allow construction of new, useful general-purpose types.
Category | Functions |
---|---|
Tuple | isTuple Tuple tuple reverse |
Flags | BitFlags isBitFlagEnum Flag No Yes |
Memory allocation | RefCounted refCounted RefCountedAutoInitialize scoped Unique |
Code generation | AutoImplement BlackHole generateAssertTrap generateEmptyFunction WhiteHole |
Nullable | Nullable nullable NullableRef nullableRef |
Proxies | Proxy rebindable Rebindable ReplaceType unwrap wrap |
Types | alignForSize Ternary Typedef TypedefType UnqualRef |
- License:
- Boost License 1.0.
- Source
- std/typecons.d
- Authors:
- Andrei Alexandrescu, Bartosz Milewski, Don Clugston, Shin Fujishiro, Kenji Hara
- Examples:
-
// value tuples alias Coord = Tuple!(int, "x", int, "y", int, "z"); Coord c; c[1] = 1; // access by index c.z = 1; // access by given name writeln(c); // Coord(0, 1, 1) // names can be omitted alias DicEntry = Tuple!(string, string); // tuples can also be constructed on instantiation writeln(tuple(2, 3, 4)[1]); // 3 // construction on instantiation works with names too writeln(tuple!("x", "y", "z")(2, 3, 4).y); // 3 // Rebindable references to const and immutable objects { class Widget { void foo() const @safe {} } const w1 = new Widget, w2 = new Widget; w1.foo(); // w1 = w2 would not work; can't rebind const object auto r = Rebindable!(const Widget)(w1); // invoke method as if r were a Widget object r.foo(); // rebind r to refer to another object r = w2; }
- struct Unique(T);
-
Encapsulates unique ownership of a resource.
When a
Unique!T
goes out of scope it will calldestroy
on the resourceT
that it manages, unless it is transferred. One important consequence ofdestroy
is that it will call the destructor of the resourceT
. GC-managed references are not guaranteed to be valid during a destructor call, but other members ofT
, such as file handles or pointers tomalloc
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 aUnique!T
outside of its original scope, then it can be transferred. The transfer can be explicit, by callingrelease
, or implicit, when returning Unique from a function. The resourceT
can be a polymorphic class object or instance of an interface, in which case Unique behaves polymorphically too.
IfT
is a value type, thenUnique!T
will be implemented as a reference to aT
.- 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) { writeln(u2.i); // 6 // Resource automatically deleted here } Unique!S u1; assert(u1.isEmpty); u1 = produce(); increment(u1); writeln(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 toT*
ifT
is a value type. - Unique!T create(A...)(auto ref A args)
Constraints: if (__traits(compiles, new T(args))); -
Allows safe construction of
Unique
. It creates the resource and guarantees unique ownership of it (unlessT
publishes aliases ofthis
).- 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)
Constraints: 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 aUnique
of base type.- Example
class C : Object {} Unique!C uc = new C; Unique!Object uo = uc.release;
- void opAssign(U)(Unique!U u)
Constraints: 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. 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 anint
and astring
.Tuple
can be used to bundle values together, notably when returning multiple values from a function. Ifobj
is aTuple
, the individual members are accessible with the syntaxobj[0]
for the first field,obj[1]
for the second, and so on.- See Also:
-
tuple
.
- 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"; writeln(e[1]); // "Hello" writeln(e[0]); // 4
- Examples:
- A
Tuple
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)));
- Examples:
- Use tuples as ranges
import std.algorithm.iteration : sum; import std.range : only; auto t = tuple(1, 2); writeln(t.expand.only.sum); // 3
- 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]); // "2" writeln(t[2]); // 42 writeln(t[3]); // true
- 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", "", ""));
- Types expand;
-
Use
t.expand
for aTuple
t
to expand it into its components. The result ofexpand
acts as if theTuple
's components were listed as a list of values. (Ordinarily, aTuple
acts as a single value.)- Examples:
-
auto t1 = tuple(1, " hello ", 'a'); writeln(t1.toString()); // `Tuple!(int, string, char)(1, " hello ", 'a')` void takeSeveralTypes(int n, string s, bool b) { assert(n == 4 && s == "test" && b == false); } auto t2 = tuple(4, "test", false); //t.expand acting as a list of values takeSeveralTypes(t2.expand);
- 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 thisTuple
, or can implicitly convert to those types. They must be in the same order as they appear inTypes
.
- Examples:
-
alias ISD = Tuple!(int, string, double); auto tup = ISD(1, "test", 3.2); writeln(tup.toString()); // `Tuple!(int, string, double)(1, "test", 3.2)`
- this(U, size_t n)(U[n] values)
Constraints: 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)
Constraints: if (areBuildCompatibleTuples!(typeof(this), U)); -
Constructor taking a compatible
Tuple
. TwoTuple
s are compatible iff they are both of the same length, and, for each typeT
on the left-hand side, the corresponding typeU
on the right-hand side can implicitly convert toT
.- Parameters:
U another
A compatible Tuple
to build from. Its type must be compatible with the targetTuple
'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)(R rhs)
Constraints: if (areCompatibleTuples!(typeof(this), R, "=="));
const bool opEquals(R)(R rhs)
Constraints: if (areCompatibleTuples!(typeof(this), R, "=="));
bool opEquals(R...)(auto ref R rhs)
Constraints: if (R.length > 1 && areCompatibleTuples!(typeof(this), Tuple!R, "==")); -
Comparison for equality. Two
Tuple
s 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 typeU
on the right-hand side, values of typeT
can be compared with values of typeU
. - For each value
v1
on the left-hand side and each valuev2
on the right-hand side, the expressionv1 == v2
is true.
- Parameters:
R rhs
The Tuple
to compare against. It must meeting the criteria for comparison betweenTuple
s.
- Returns:
- true if both
Tuple
s are equal, otherwise false.
- 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 writeln(t1); // t2
- Each
- int opCmp(R)(R rhs)
Constraints: if (areCompatibleTuples!(typeof(this), R, "<"));
const int opCmp(R)(R rhs)
Constraints: if (areCompatibleTuples!(typeof(this), R, "<")); -
Comparison for ordering.
- Parameters:
R rhs
The Tuple
to compare against. It must meet the criteria for comparison betweenTuple
s.
- Returns:
- For any values
v1
on the right-hand side andv2
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.
- A negative integer if the expression
- Examples:
- The first
v1
for whichv1 > 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); //Only the first result matters for comparison tup1[0] = 2; assert(tup1 > tup2);
- auto opBinary(string op, T)(auto ref T t)
Constraints: if (op == "~" && !(is(T : U[], U) && isTuple!U));
auto opBinaryRight(string op, T)(auto ref T t)
Constraints: 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 oft
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)
Constraints: if (areCompatibleTuples!(typeof(this), R, "=")); -
Assignment from another
Tuple
.- Parameters:
R rhs
The source Tuple
to assign from. Each element of the sourceTuple
must be implicitly assignable to each respective element of the targetTuple
.
- inout ref auto rename(names...)() return
Constraints: if (names.length == 0 || allSatisfy!(isSomeString, typeof(names))); -
Renames the elements of a
Tuple
.rename
uses the passednames
and returns a newTuple
using these names, with the content unchanged. If fewer names are passed than there are members of theTuple
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 theTuple
.- Examples:
-
auto t0 = tuple(4, "hello"); auto t0Named = t0.rename!("val", "tag"); writeln(t0Named.val); // 4 writeln(t0Named.tag); // "hello" Tuple!(float, "dat", size_t[2], "pos") t1; t1.pos = [2, 1]; auto t1Named = t1.rename!"height"; t1Named.height = 3.4f; writeln(t1Named.height); // 3.4f writeln(t1Named.pos); // [2, 1] t1Named.rename!"altitude".altitude = 5; writeln(t1Named.height); // 5 Tuple!(int, "a", int, int, "c") t2; t2 = tuple(3,4,5); auto t2Named = t2.rename!("", "b"); // "a" no longer has a name static assert(!__traits(hasMember, typeof(t2Named), "a")); writeln(t2Named[0]); // 3 writeln(t2Named.b); // 4 writeln(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; writeln(res); // 68 const tup = Tuple!(int, "a", int, "b")(2, 3); const renamed = tup.rename!("c", "d"); writeln(renamed.c + renamed.d); // 5
- inout ref auto rename(alias translate)()
Constraints: 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 intranslate
must be the name of a member of thetuple
. The same rules for empty strings apply as for the variadic template overload ofrename
.- 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; writeln(t1Named.pos); // [2, 1] t1Named.rename!(["height": "altitude"]).altitude = 5; writeln(t1Named.height); // 5 Tuple!(int, "a", int, "b") t2; t2 = tuple(3, 4); auto t2Named = t2.rename!(["a": "b", "b": "c"]); writeln(t2Named.b); // 3 writeln(t2Named.c); // 4 const t3 = Tuple!(int, "a", int, "b")(3, 4); const t3Named = t3.rename!(["a": "b", "b": "c"]); writeln(t3Named.b); // 3 writeln(t3Named.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; writeln(t1Named.pos); // [2, 1] t1Named.rename!([0: "altitude"]).altitude = 5; writeln(t1Named.height); // 5 Tuple!(int, "a", int, "b", int, "c") t2; t2 = tuple(3, 4, 5); auto t2Named = t2.rename!([0: "c", 2: "a"]); writeln(t2Named.a); // 5 writeln(t2Named.b); // 4 writeln(t2Named.c); // 3
- inout @property ref @trusted inout(Tuple!(sliceSpecs!(from, to))) slice(size_t from, size_t to)()
Constraints: 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); // https://issues.dlang.org/show_bug.cgi?id=15645 Tuple!(int, short, bool, double) b; static assert(!__traits(compiles, b.slice!(2, 4)));
- const nothrow @safe size_t toHash();
-
Creates a hash of this
Tuple
.- Returns:
- A
size_t
representing the hash of thisTuple
.
- const string toString()();
-
Converts to string.
- Returns:
- The string representation of this
Tuple
.
- const void toString(DG)(scope DG sink);
const 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 expandedTuple
, so it may contain as many formats as theTuple
has fields.%(inner%|sep%)
The format
inner
is one format, that is applied on all fields of theTuple
. The inner format must be compatible to all of them.- Parameters:
DG sink
A char
accepting delegateFormatSpec!Char fmt
A std.format.FormatSpec
- Examples:
-
import std.format : format; Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ]; // Default format writeln(format("%s", tuple("a", 1))); // `Tuple!(string, int)("a", 1)` // One Format for each individual component writeln(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10))); // `0x1 v 1.0000 w 0xa` writeln(format("%#x v %.4f w %#x", tuple(1, 1.0, 10).expand)); // `0x1 v 1.0000 w 0xa` // One Format for all components // `>abc< & >1< & >2.3< & >[4, 5]<` writeln(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5]))); // Array of Tuples writeln(format("%(%(f(%d) = %.1f%); %)", tupList)); // `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`
- Examples:
-
import std.exception : assertThrown; import std.format : format, FormatException; // Error: %( %) missing. assertThrown!FormatException( format("%d, %f", tuple(1, 2.0)) == `1, 2.0` ); // Error: %( %| %) missing. assertThrown!FormatException( format("%d", tuple(1, 2)) == `1, 2` ); // Error: %d inadequate for double assertThrown!FormatException( format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0` );
- auto reverse(T)(T t)
Constraints: 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"); writeln(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
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 byArgs
. 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]); // 5 writeln(value[1]); // 6.7 writeln(value[2]); // "hello" // Field names can be provided. auto entry = tuple!("index", "value")(4, "Hello"); writeln(entry.index); // 4 writeln(entry.value); // "Hello"
- auto tuple(Args...)(Args args);
-
- Parameters:
Args args
Values to initialize the Tuple
with. TheTuple
'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 ifT
is an instance ofstd.typecons.Tuple
.- Parameters:
T The type to check.
- Returns:
- true if
T
is aTuple
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 typeT
, except that you can reassign it to refer to another object. For completeness,Rebindable!(T)
aliases itself away toT
ifT
is a non-const object type.You may want to use
Rebindable
when you want to have mutable storage referring toconst
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 withcast
.- 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() @safe const { return x; } } const a = new Widget; // Fine a.y(); // error! can't modify const a // a.x = 5; // error! can't modify const a // a = new Widget;
- Examples:
- However,
Rebindable!(Widget)
does allow reassignment, while otherwise behaving exactly like aconst Widget
.class Widget { int x; int y() const @safe { return x; } } auto a = Rebindable!(const Widget)(new Widget); // Fine a.y(); // error! can't modify const a // a.x = 5; // Fine a = new Widget;
- Rebindable!T rebindable(T)(T obj)
Constraints: 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.
- Examples:
-
class C { int payload; this(int p) { payload = p; } } const c = new C(1); auto c2 = c.rebindable; writeln(c2.payload); // 1 // passing Rebindable to rebindable c2 = c2.rebindable; c2 = new C(2); writeln(c2.payload); // 2 const c3 = c2.get; writeln(c3.payload); // 2
- 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 aRebindable
.- 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); // 1 // passing Rebindable to rebindable c2 = c2.rebindable; writeln(c2.payload); // 1
- 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
orclass
.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
orclass
.
- 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. Callingnullify
can nullify it again.Practically
Nullable!T
stores aT
and abool
.- 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.get.address; auto customerNum = queryResult.get.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); writeln(a.get); // 42 a.nullify(); assert(a.isNull); assertThrown!Throwable(a.get);
- inout this(inout T value);
-
Constructor initializing
this
withvalue
.- Parameters:
T value
The value to initialize this Nullable
with.
- const bool opEquals()(auto ref const(typeof(this)) rhs);
const bool opEquals(U)(auto ref const(U) rhs)
Constraints: if (!is(U : typeof(this)) && 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); // empty writeln(empty); // Nullable!int.init assert(empty != a); assert(empty != b); assert(empty != c); writeln(a); // b assert(a != c); assert(empty != 42); writeln(a); // 42 assert(c != 42);
- string toString();
const string toString();
void toString(W)(ref W writer, ref scope const FormatSpec!char fmt)
Constraints: if (isOutputRange!(W, char));
const void toString(W)(ref W writer, ref scope const FormatSpec!char fmt)
Constraints: if (isOutputRange!(W, char)); -
Gives the string
"Nullable.null"
ifisNull
istrue
. Otherwise, the result is equivalent to callingstd.format.formattedWrite
on the underlying value.- Parameters:
W writer
A char
accepting output rangeFormatSpec!char fmt
A std.format.FormatSpec
which is used to represent the value if this Nullable is not null
- Returns:
- A
string
ifwriter
andfmt
are not set;void
otherwise.
- const pure nothrow @property @safe bool isNull();
-
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);
- void nullify()();
-
Forces
this
to the null state.- Examples:
-
Nullable!int ni = 0; assert(!ni.isNull); ni.nullify(); assert(ni.isNull);
- void opAssign()(T value);
-
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 thisNullable
.
- void opAssign()(Nullable!T value);
-
If value is null, sets this to null, otherwise assigns
value.get
to the internally-held state. If the assignment succeeds,this
becomes non-null.- Parameters:
Nullable!T value
A value of type Nullable!T
to assign to thisNullable
.
- 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 typeT
, and the resulting code will look very strange. It is strongly recommended that this be avoided by instead using the version ofNullable
that takes an additionalnullValue
template argument.//Passes Nullable!(int*) npi; assert(npi.isNull); //Passes?! npi = null; assert(!npi.isNull);
- inout pure nothrow @property ref @safe inout(T) get();
inout pure nothrow @property @safe inout(T) get()(inout(T) fallback);
inout pure nothrow @property @safe auto get(U)(inout(U) fallback); -
Gets the value if not null. If
this
is in the null state, and the optional parameterfallback
was provided, it will be returned. Withoutfallback
, callingget
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
.
- struct Nullable(T, T nullValue);
auto nullable(alias nullValue, T)(T t)
Constraints: 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 valueuint.max
to denote a null state.Nullable!(T, nullValue)
is more storage-efficient thanNullable!T
because it does not need to store an extrabool
.- Parameters:
T The wrapped type for which Nullable provides a null value. nullValue The null value which denotes the null state of this Nullable
. Must be of typeT
.
- 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); writeln(a); // 5 static assert(a.sizeof == int.sizeof);
- Examples:
-
auto a = nullable!(int.min)(8); writeln(a); // 8 a.nullify(); assert(a.isNull);
- this(T value);
-
Constructor initializing
this
withvalue
.- 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 the null state, otherwise false.
- 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()(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 leavethis
in the null state.- Parameters:
T value
A value of type T
to assign to thisNullable
. If it isnullvalue
, then the internal state of thisNullable
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 thisNullable
is no different than assigning any other value of typeT
, and the resulting code will look very strange. It is strongly recommended that this be avoided by usingT
's "built in" null value fornullValue
.//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 toT
.- Preconditions
-
isNull
must befalse
.
- 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);
- 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 theNullable
, pass it to the function you provide and wrap the result in anotherNullable
(if necessary). If theNullable
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
, elseNullable.init
. See also: TheMaybe
monad
- Examples:
-
alias toFloat = i => cast(float) i; Nullable!int sample; // apply(null) results in a null `Nullable` of the function's return type. Nullable!float f = sample.apply!toFloat; assert(sample.isNull && f.isNull); sample = 3; // apply(non-null) calls the function and wraps the result in a `Nullable`. f = sample.apply!toFloat; assert(!sample.isNull && !f.isNull); writeln(f.get); // 3.0f
- Examples:
-
alias greaterThree = i => (i > 3) ? i.nullable : Nullable!(typeof(i)).init; Nullable!int sample; // when the function already returns a `Nullable`, that `Nullable` is not wrapped. auto result = sample.apply!greaterThree; assert(sample.isNull && result.isNull); // The function may decide to return a null `Nullable`. sample = 3; result = sample.apply!greaterThree; assert(!sample.isNull && result.isNull); // Or it may return a value already wrapped in a `Nullable`. sample = 4; result = sample.apply!greaterThree; assert(!sample.isNull && !result.isNull); writeln(result.get); // 4
- 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. InternallyNullableRef!T
only stores a pointer toT
(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); // 5 writeln(x); // 5 a = 42; writeln(x); // 42 assert(!a.isNull); writeln(a); // 42 a.nullify(); writeln(x); // 42 assert(a.isNull); assertThrown!Throwable(a.get); assertThrown!Throwable(a = 71); a.bind(&y); writeln(a); // 7 y = 135; writeln(a); // 135
- pure nothrow @safe this(T* value);
-
Constructor binding
this
tovalue
.- 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 thisNullableRef
to.
- Examples:
-
NullableRef!int nr = new int(42); writeln(nr); // 42 int* n = new int(1); nr.bind(n); writeln(nr); // 1
- const pure nothrow @property @safe bool isNull();
-
Returns
true
if and only ifthis
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)
Constraints: if (isAssignable!T); -
Assigns
value
to the internally-held state.- Parameters:
T value
A value of type T
to assign to thisNullableRef
. If the internal state of thisNullableRef
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); // 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 toT
.- 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 ofBase
which automatically implements all abstract member functions inBase
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:
-
AutoImplement
,generateEmptyFunction
- 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); writeln(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 ofBase
which automatically implements all abstract member functions as functions that always fail. These functions simply throw anError
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:
-
AutoImplement
,generateAssertTrap
- 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) 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 interfaceBase
in specified way.The second version of
AutoImplement
automatically implementsInterface
, while deriving fromBaseClass
.- Parameters:
how template which specifies how functions will be implemented/overridden. Two arguments are passed to how
: the typeBase
and an alias to an implemented function. Thenhow
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 inBase
. Thenwhat
must return a boolean value. Returntrue
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 outsidestd.typecons
cannot be used in the generated code. To workaround this problem, you mayimport
necessary things in a local struct, as done in thegenerateLogger()
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/orwhat
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; // for all implemented methods: // - try default first // - only on a failure run & return fallback enum fallback = q{ scope (failure) return fallback.%1$s(args); return default_.%1$s(args); }.format(__traits(identifier, func)); } // combines two classes and use the second one as fallback 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()); // -1 writeln(registry.bar()); // 2
- template generateEmptyFunction(C, func...)
enum string generateAssertTrap(C, func...); -
Predefined how-policies for
AutoImplement
. These templates are also used byBlackHole
andWhiteHole
, respectively.- Examples:
-
alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction); interface I { int foo(); string bar(); } auto i = new BlackHole!I(); // generateEmptyFunction returns the default value of the return type without doing anything writeln(i.foo); // 0 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(); // generateAssertTrap throws an exception for every unimplemented function of the interface 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 theinterface
Targets
, wrap creates an internal wrapper class which inheritsTargets
and wraps thesrc
object, then returns it.
unwrap
can be used to extract objects which have been wrapped bywrap
.- 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 // structural downcast (two steps) Quack qy = fx.unwrap!Quack; // Flyer -> Quack Human hy = qy.unwrap!Human; // Quack -> Human assert(hy is h1); // structural downcast (one step) Human hz = fx.unwrap!Human; // Flyer -> 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()); // 1 writeln(b.stop()); // 2 writeln(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 ofRefCounted
below).- Examples:
-
import core.exception : AssertError; import std.exception : assertThrown; struct Foo { int a = 42; } RefCounted!(Foo, RefCountedAutoInitialize.yes) rcAuto; RefCounted!(Foo, RefCountedAutoInitialize.no) rcNoAuto; writeln(rcAuto.refCountedPayload.a); // 42 assertThrown!AssertError(rcNoAuto.refCountedPayload); rcNoAuto.refCountedStore.ensureInitialized; writeln(rcNoAuto.refCountedPayload.a); // 42
- 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.An instance of
RefCounted
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 theT
payload.RefCounted
usesmalloc
to allocate the store. As instances ofRefCounted
are copied or go out of scope, they will automatically increment or decrement the reference count. When the reference count goes down to zero,RefCounted
will calldestroy
against the payload and callfree
to deallocate the store. If theT
payload contains any references to GC-allocated memory, thenRefCounted
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 ofdestroy
is that it will call the destructor of theT
payload. GC-managed references are not guaranteed to be valid during a destructor call, but other members ofT
, such as file handles or pointers tomalloc
memory, will still be valid during the destructor call. This allows theT
to deallocate or clean up any non-GC resources immediately after the reference count has reached zero.
RefCounted
is unsafe and should be used with care. No references to the payload should be escaped outside theRefCounted
object.
TheautoInit
option makes the object ensure the store is automatically initialized. LeavingautoInit == RefCountedAutoInitialize.yes
(the default option) is convenient but has the cost of a test whenever the payload is accessed. IfautoInit == RefCountedAutoInitialize.no
, user code must call eitherrefCountedStore.isInitialized
orrefCountedStore.ensureInitialized
before attempting to access the payload. Not doing so results in null pointer dereference.- Examples:
-
// A pair of an `int` and a `size_t` - the latter being the // reference count - will be dynamically allocated auto rc1 = RefCounted!int(5); writeln(rc1); // 5 // No more allocation, add just one extra reference count auto rc2 = rc1; // Reference semantics rc2 = 42; writeln(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)
Constraints: if (A.length > 0);
this(T val); -
Constructor that initializes the payload.
- Postcondition
-
refCountedStore.isInitialized
- void opAssign(typeof(this) rhs);
void opAssign(T rhs); -
Assignment operators
- @property ref @trusted T refCountedPayload() return;
inout pure nothrow @nogc @property ref @safe inout(T) refCountedPayload() return; -
Returns a reference to the payload. If (autoInit == RefCountedAutoInitialize.yes), calls
refCountedStore.ensureInitialized
. Otherwise, just issuesassert(refCountedStore.isInitialized)
. Used withalias refCountedPayload this;
, so callers can just use theRefCounted
object as aT
.The first overload exists only if
autoInit == RefCountedAutoInitialize.yes
. So ifautoInit == 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)(T val);
-
Initializes a
RefCounted
withval
. The template parameterT
ofRefCounted
is inferred fromval
. This function can be used to move non-copyable values to the heap. It also disables theautoInit
option ofRefCounted
.- Parameters:
T val
The value to be reference counted
- Returns:
- An initialized
RefCounted
containingval
.
- See Also:
- C++'s make_shared
- Examples:
-
static struct File { string name; @disable this(this); // not copyable ~this() { name = null; } } auto file = File("name"); writeln(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)); writeln(rcFile.name); // "name" writeln(file.name); // null auto rcFile2 = rcFile; writeln(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 itema
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; writeln(n); // 11 writeln(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 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++; writeln(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.- 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
- Note
- If 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); // 11
- 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); // ok static assert(!__traits(compiles, takeMyInt(i))); MyInt myInt; static assert(!__traits(compiles, takeInt(myInt))); takeMyInt(myInt); // ok
- Examples:
- Use the optional
cookie
argument to create different types of the same base typealias 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));
- string toString(this T)();
void toString(this T, W)(ref W writer, ref scope const FormatSpec!char fmt)
Constraints: 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); // td.to!string
- template TypedefType(T)
-
Get the underlying type which a
Typedef
wraps. IfT
is not aTypedef
it will alias itself toT
.- Examples:
-
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) ); writeln(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 ofnew
. 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 parentclass
orstruct
, just like a child struct instance. Scoped member variables must have typetypeof(scoped!Class(args))
, and be initialized with a call to scoped. 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; writeln(aRef.x); // 42 // Scoped destruction { auto a2 = scoped!A(1); writeln(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; writeln(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); writeln(b2.a.x); // 6 destroy(*b2); // calls A's destructor for b2.a
- @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. UsingFlag!"Name"
instead ofbool
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 callinggetLine
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 structsYes
andNo
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 unstructuredbool
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.- Examples:
-
Flag!"abc" flag; writeln(flag); // Flag!"abc".no writeln(flag); // No.abc assert(!flag); if (flag) assert(0);
- Examples:
-
auto flag = Yes.abc; assert(flag); writeln(flag); // Yes.abc if (!flag) assert(0); if (flag) {} else assert(0);
- enum Flag: bool;
-
- no
-
When creating a value of type
Flag!"Name"
, useFlag!"Name".no
for the negative option. When using a value of typeFlag!"Name"
, compare it againstFlag!"Name".no
or justfalse
or0
. - yes
-
When creating a value of type
Flag!"Name"
, useFlag!"Name".yes
for the affirmative option. When using a value of typeFlag!"Name"
, compare it againstFlag!"Name".yes
.
- struct Yes;
struct No; -
Convenience names that allow using e.g.
Yes.encryption
instead ofFlag!"encryption".yes
andNo.encryption
instead ofFlag!"encryption".no
.- Examples:
-
Flag!"abc" flag; writeln(flag); // Flag!"abc".no writeln(flag); // No.abc assert(!flag); if (flag) assert(0);
- Examples:
-
auto flag = Yes.abc; assert(flag); writeln(flag); // Yes.abc if (!flag) assert(0); if (flag) {} else assert(0);
- 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 // D == 3 } 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; // will throw SwitchError 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, } // A default constructed BitFlags has no value set immutable BitFlags!Enum flags_empty; assert(!flags_empty.A); // Value can be set with the | operator immutable flags_A = flags_empty | Enum.A; // and tested using property access assert(flags_A.A); // or the & operator assert(flags_A & Enum.A); // which commutes. 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); // Use the ~ operator for subtracting flags immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A); assert(!flags_B.A && flags_B.B && !flags_B.C); // use & between BitFlags for intersection writeln(flags_B); // (flags_BC & flags_AB)
- 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); // (flags_empty | flags_AB) temp = flags_empty; temp |= Enum.B; writeln(temp); // (flags_empty | Enum.B) temp = flags_empty; temp &= flags_AB; writeln(temp); // (flags_empty & flags_AB) temp = flags_empty; temp &= Enum.A; writeln(temp); // (flags_empty & Enum.A)
- Examples:
- Conversion to bool and int
enum Enum { A = 1 << 0, B = 1 << 1, } BitFlags!Enum flags; // BitFlags with no value set evaluate to false assert(!flags); // BitFlags with at least one value set evaluate to true flags |= Enum.A; assert(flags); // This can be useful to check intersection between BitFlags BitFlags!Enum flags_AB = Enum.A | Enum.B; assert(flags & flags_AB); assert(flags & Enum.A); // You can of course get you raw value out of flags auto value = cast(int) flags; writeln(value); // Enum.A
- Examples:
- You need to specify the
unsafe
parameter for enums with custom valuesenum UnsafeEnum { A = 1, B = 2, C = 4, BC = B|C } static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags; })); BitFlags!(UnsafeEnum, Yes.unsafe) flags; // property access tests for exact match of unsafe enums flags.B = true; assert(!flags.BC); // only B flags.C = true; assert(flags.BC); // both B and C flags.B = false; assert(!flags.BC); // only C // property access sets all bits of unsafe enum group 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
intoTo
, in one or more typesT
. 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 instruct
s orclass
es 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
instd.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 totrue
.- 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[])) );
- struct Ternary;
-
Ternary type with three truth values:
-
Ternary.yes
fortrue
-
Ternary.no
forfalse
-
Ternary.unknown
as an unknown state
Also known as trinary, trivalent, or trilean.- See Also:
- Three Valued Logic on Wikipedia
- Examples:
-
Ternary a; writeln(a); // Ternary.unknown writeln(~Ternary.yes); // Ternary.no writeln(~Ternary.no); // Ternary.yes writeln(~Ternary.unknown); // 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
, receivingno
forfalse
andyes
fortrue
. - pure nothrow @nogc @safe this(const Ternary b);
-
Construct a ternary value from another ternary value
- Ternary opUnary(string s)()
Constraints: if (s == "~");
Ternary opBinary(string s)(Ternary rhs)
Constraints: if (s == "|");
Ternary opBinary(string s)(Ternary rhs)
Constraints: if (s == "&");
Ternary opBinary(string s)(Ternary rhs)
Constraints: if (s == "^");
Ternary opBinary(string s)(bool rhs)
Constraints: 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
-
© 1999–2021 The D Language Foundation
Licensed under the Boost License 1.0.
https://dlang.org/phobos/std_typecons.html