This module implements a variety of type constructors, i.e., templatesthat allow construction of new, useful general-purpose types.
Encapsulates unique ownership of a resource.
When a
Unique!T goes out of scope it will call
destroyon the resource
T that it manages, unless it is transferred.One important consequence of
destroy is that it will call thedestructor of the resource
T. GC-managed references are notguaranteed to be valid during a destructor call, but other members of
T, such as file handles or pointers to
malloc memory, willstill be valid during the destructor call. This allows the resource
T to deallocate or clean up any non-GC resources.
If it is desirable to persist a
Unique!T outside of its originalscope, then it can be transferred. The transfer can be explicit, bycalling
release, or implicit, when returning Unique from afunction. The resource
T can be a polymorphic class object orinstance of an interface, in which case Unique behaves polymorphicallytoo.
If
T is a value type, then
Unique!T will be implementedas a reference to a
T.
Examples:struct S{int i;this(int i){this.i = i;}}Unique!S produce(){// Construct a unique instance of S on the heapUnique!S ut =new S(5);// Implicit transfer of ownershipreturn ut;}// Borrow a unique resource by refvoid increment(refUnique!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();writeln(u1.i);// 5increment(u1);writeln(u1.i);// 6//consume(u1); // Error: u1 is not copyable// Transfer ownership of the resourceconsume(u1.release);assert(u1.isEmpty); Represents a reference toT. Resolves toT* ifT is a value type.
Unique!T
create(A...)(auto ref A
args)
if (__traits(compiles, new T(args)));
Allows safe construction ofUnique. It creates the resource and guarantees unique ownership of it (unlessT publishes aliases ofthis).
NoteNested structs/classes cannot be created.
Parameters:Aargs | Arguments to pass toT's constructor.staticclass C {}auto u = Unique!(C).create(); |
Constructor that takes an rvalue. It will ensure uniqueness, as long as the rvalue isn't just a view on an lvalue (e.g., a cast). Typical usage:
Unique!Foo f =new Foo;
Constructor that takes an lvalue. It nulls its source. The nulling will ensure uniqueness as long as there are no previous aliases to the source.
this
(U)(Unique!U
u)
if (is(u.RefT : RefT));
Constructor that takes aUnique of a type that is convertible to our type.
Typically used to transfer aUnique 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)
if (is(u.RefT : RefT));
Transfer ownership from aUnique of a type that is convertible to our type.
@property bool
isEmpty() const;
Returns whether the resource exists.
Transfer ownership to aUnique rvalue. Nullifies the current contents. Same as calling std.algorithm.move on it.
struct
Tuple(Specs...) if (distinctFieldNames!Specs);
Tuple of values, for exampleTuple!(int, string) is a record thatstores anint and astring.Tuple can be used to bundlevalues together, notably when returning multiple values from afunction. Ifobj is aTuple, the individual members areaccessible with the syntaxobj[0] for the first field,obj[1]for the second, and so on.
Parameters:| Specs | A list of types (and optionally, member names) that theTuple contains. |
Examples:Tuple!(int,int) point;// assign coordinatespoint[0] = 5;point[1] = 6;// read coordinatesauto 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 a
Tuple with unnamed fields, i.e. each naming imparts a separate type for the
Tuple. Two
Tuples 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);staticassert(is(t.Types == AliasSeq!(int, string,ushort,bool)));writeln(t[1]);// "2"writeln(t[2]);// 42writeln(t[3]);// true
alias
Types = staticMap!(extractType, fieldSpecs);
The types of theTuple's components.
alias
fieldNames = staticMap!(extractName, fieldSpecs);
The names of theTuple's components. Unnamed fields have empty names.
Examples:import std.meta : AliasSeq;alias Fields = Tuple!(int,"id", string,float);staticassert(Fields.fieldNames == AliasSeq!("id","","")); Uset.expand for aTuplet 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 valuestakeSeveralTypes(t2.expand); Constructor taking one value for each field.
Parameters:Typesvalues | A list of values that are either the same types as those given by theTypes 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)
if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types));
Constructor taking a compatible array.
Parameters:U[n]values | A compatible static array to build theTuple from. Array slices are not supported. |
Examples:int[2] ints;Tuple!(int,int) t = ints;
this
(U)(U
another)
if (areBuildCompatibleTuples!(typeof(this), U) && (noMemberHasCopyCtor!(typeof(this)) || !is(Unqual!U == Unqual!(typeof(this)))));
Constructor taking a compatibleTuple. TwoTuples are compatibleiff 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:Uanother | A compatibleTuple 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 doubleDubVec dv = iv;//Error: double cannot implicitly convert to int//IntVec iv2 = dv;
bool
opEquals(R)(R
rhs)
if (areCompatibleTuples!(typeof(this), R, "=="));
bool
opEquals(R)(R
rhs) const
if (areCompatibleTuples!(typeof(this), R, "=="));
bool
opEquals(R...)(auto ref R
rhs)
if (R.length > 1 && areCompatibleTuples!(typeof(this), Tuple!R, "=="));
Comparison for equality. TwoTuples are considered equaliff they fulfill the following criteria:
- EachTuple is the same length.
- For each typeT 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 valuev1 on the left-hand side and each valuev2 on the right-hand side, the expressionv1 == v2 is true.
Parameters:Rrhs | TheTuple to compare against. It must meeting the criteria for comparison betweenTuples. |
Returns:true if bothTuples 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 1writeln(t1);// t2
auto
opCmp(R)(R
rhs)
if (areCompatibleTuples!(typeof(this), R, "<"));
auto
opCmp(R)(R
rhs) const
if (areCompatibleTuples!(typeof(this), R, "<"));
Comparison for ordering.
Parameters:Rrhs | TheTuple to compare against. It must meet the criteria for comparison betweenTuples. |
Returns:For any values
v1 contained by the left-hand side tuple and any values
v2 contained by the right-hand side:
0 if
v1 == v2 for all members or the following value for the first position were the mentioned criteria is not satisfied:
- NaN, in case one of the operands is a NaN.
- A negative number if the expressionv1 < v2 is true.
- A positive number if the expressionv1 > v2 is true.
Examples:The first
v1 for which
v1 > v2 is true determines the result. This could lead to unexpected behaviour.
auto tup1 = tuple(1, 1, 1);auto tup2 = tuple(1, 100, 100);assert(tup1 < tup2);//Only the first result matters for comparisontup1[0] = 2;assert(tup1 > tup2);
auto
opBinary(string op, T)(auto ref T
t)
if (op == "~" && !(is(T : U[], U) && isTuple!U));
auto
opBinaryRight(string op, T)(auto ref T
t)
if (op == "~" && !(is(T : U[], U) && isTuple!U));
Concatenate Tuples. Tuple concatenation is only allowed if all named fields are distinct (no named field of this tuple occurs int and no named field oft occurs in this tuple).
Parameters:Tt | TheTuple to concatenate with |
Returns:A concatenation of this tuple andt
ref Tuple
opAssign(R)(auto ref R
rhs)
if (areCompatibleTuples!(typeof(this), R, "="));
Assignment from anotherTuple.
Parameters:Rrhs | The sourceTuple to assign from. Each element of the sourceTuple must be implicitly assignable to each respective element of the targetTuple. |
ref auto
rename(names...)() inout return
if (names.length == 0 || allSatisfy!(isSomeString, typeof(names)));
Renames the elements of a
Tuple.
rename uses the passed
names and returns a 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");writeln(t0Named.val);// 4writeln(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.4fwriteln(t1Named.pos);// [2, 1]t1Named.rename!"altitude".altitude = 5;writeln(t1Named.height);// 5Tuple!(int,"a",int,int,"c") t2;t2 = tuple(3,4,5);auto t2Named = t2.rename!("","b");// "a" no longer has a namestaticassert(!__traits(hasMember,typeof(t2Named),"a"));writeln(t2Named[0]);// 3writeln(t2Named.b);// 4writeln(t2Named.c);// 5// not allowed to specify more names than the tuple has membersstaticassert(!__traits(compiles, t2.rename!("a","b","c","d")));// use it in a range pipelineimport 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);// 68const tup = Tuple!(int,"a",int,"b")(2, 3);const renamed = tup.rename!("c","d");writeln(renamed.c + renamed.d);// 5 ref auto
rename(alias translate)() inout
if (is(typeof(translate) : V[K], V, K) && isSomeString!V && (isSomeString!K || is(K : size_t)));
Overload of
rename that takes an associative 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 nameTuple!(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);// 5Tuple!(int,"a",int,"b") t2;t2 = tuple(3, 4);auto t2Named = t2.rename!(["a":"b","b":"c"]);writeln(t2Named.b);// 3writeln(t2Named.c);// 4const t3 = Tuple!(int,"a",int,"b")(3, 4);const t3Named = t3.rename!(["a":"b","b":"c"]);writeln(t3Named.b);// 3writeln(t3Named.c);// 4
Examples://replace names by their positionTuple!(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);// 5Tuple!(int,"a",int,"b",int,"c") t2;t2 = tuple(3, 4, 5);auto t2Named = t2.rename!([0:"c", 2:"a"]);writeln(t2Named.a);// 5writeln(t2Named.b);// 4writeln(t2Named.c);// 3
@property ref @trusted inout(Tuple!(sliceSpecs!(from, to)))
slice(size_t from, size_t to)() inout
if (from <= to && (to <= Types.length));
Takes a slice by-reference of thisTuple.
Parameters:| from | Asize_t designating the starting position of the slice. |
| to | Asize_t designating the ending position (exclusive) of the slice. |
Returns:A newTuple 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);staticassert(is(typeof(s) == Tuple!(string,float)));assert(s[0] =="abc" && s[1] == 4.5);// https://issues.dlang.org/show_bug.cgi?id=15645Tuple!(int,short,bool,double) b;staticassert(!__traits(compiles, b.slice!(2, 4)));
nothrow @safe size_t
toHash() const;
Creates a hash of thisTuple.
Returns:Asize_t representing the hash of thisTuple.
Converts to string.
Returns:The string representation of thisTuple.
void
toString(DG)(scope DG
sink);
void
toString(DG, Char)(scope DG
sink, ref scope const FormatSpec!Char
fmt);
FormatsTuple with either%s,%(inner%) or%(inner%|sep%).
Formats supported by Tuple| Format | Description |
|---|
%s | Format likeTuple!(types)(elements formatted with %s each). |
%(inner%) | The formatinner is applied the expandedTuple, so it may contain as many formats as theTuple has fields. |
%(inner%|sep%) | The formatinner is one format, that is applied on all fields of theTuple. The inner format must be compatible to all of them. |
Examples:import std.format : format;Tuple!(int,double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];// Default formatwriteln(format("%s", tuple("a", 1)));// `Tuple!(string, int)("a", 1)`// One Format for each individual componentwriteln(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 Tupleswriteln(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 doubleassertThrown!FormatException( format("%(%d%|, %)", tuple(1, 2.0)) ==`1, 2.0`);
auto
reverse(T)(T
t)
if (isTuple!T);
Creates a copy of a
Tuple with its fields in reverse order.
Examples:auto tup = tuple(1,"2");writeln(tup.reverse);// tuple("2", 1) Constructs a
Tuple object instantiated and initialized according to the given arguments.
Parameters:| Names | An optional list of strings naming each successive field of theTuple 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]);// 5writeln(value[1]);// 6.7writeln(value[2]);// "hello"// Field names can be provided.auto entry =tuple!("index","value")(4,"Hello");writeln(entry.index);// 4writeln(entry.value);// "Hello" auto
tuple(Args...)(Args
args);
Parameters:Argsargs | Values to initialize theTuple with. TheTuple's type will be inferred from the types of the values given. |
Returns:A newTuple with its type inferred from the arguments given.
Returnstrue if and only ifT is an instance ofstd.typecons.Tuple.
Returns:true ifT is aTuple type, false otherwise.
Examples:staticassert(isTuple!(Tuple!()));staticassert(isTuple!(Tuple!(int)));staticassert(isTuple!(Tuple!(int,real, string)));staticassert(isTuple!(Tuple!(int,"x",real,"y")));staticassert(isTuple!(Tuple!(int, Tuple!(real), string)));
template
Rebindable(T) if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
struct
Rebindable(T) if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T);
Rebindable!(T) is a simple, efficient wrapper that behaves justlike an object of typeT, except that you can reassign it torefer to another object. For completeness,Rebindable!(T) aliasesitself away toT ifT is a non-const object type.
You may want to useRebindable when you want to have mutablestorage referring toconst objects, for example an array ofreferences that must be sorted in place.Rebindable does notbreak the soundness of D's type system and does not incur any of therisks usually associated withcast.
Examples:Regular
const object references cannot be reassigned.
class Widget {int x;int y() @safeconst {return x; } }const a =new Widget;// Finea.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 @safe {return x; } }auto a =Rebindable!(const Widget)(new Widget);// Finea.y();// error! can't modify const a// a.x = 5;// Finea =new Widget; Examples:Using Rebindable in a generic algorithm:
import std.range.primitives : front, popFront;// simple version of std.algorithm.searching.maxElementtypeof(R.init.front) maxElement(R)(R r){auto max = rebindable(r.front); r.popFront;foreach (e; r)if (e > max) max = e;// Rebindable allows const-correct reassignmentreturn max;}struct S{char[] arr;alias arrthis;// for comparison}// can't convert to mutableconst S cs;staticassert(!__traits(compiles, { S s = cs; }));alias CS =const S;CS[] arr = [CS("harp"), CS("apple"), CS("pot")];CS ms = maxElement(arr);writeln(ms.arr);// "pot" Examples:staticstruct S{int* ptr;}S s = S(newint);const cs = s;// Can't assign s.ptr to cs.ptrstaticassert(!__traits(compiles, {s = cs;}));Rebindable!(const S) rs = s;assert(rs.ptris s.ptr);// rs.ptr is conststaticassert(!__traits(compiles, {rs.ptr =null;}));// Can't assign s.ptr to rs.ptrstaticassert(!__traits(compiles, {s = rs;}));const S cs2 = rs;// Rebind rsrs = cs2;rs = S();assert(rs.ptrisnull); Rebindable!T
rebindable(T)(T
obj)
if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T);
Rebindable!T
rebindable(T)(T
value)
if (!is(T == class) && !is(T == interface) && !isDynamicArray!T && !isAssociativeArray!T && !is(T : Rebindable!U, U));
Convenience function for creating aRebindable using automatic typeinference.
Parameters:Tobj | A reference to a value to initialize theRebindable with. |
Returns:A newly constructedRebindable 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 rebindablec2 = c2.rebindable;c2 =new C(2);writeln(c2.payload);// 2const c3 = c2.get;writeln(c3.payload);// 2 Examples:immutablestruct S{int[] array;}auto s1 = [3].idup.rebindable;s1 = [4].idup.rebindable;writeln(s1);// [4] Rebindable!T
rebindable(T)(Rebindable!T
obj);
This function simply returns theRebindable object passed in. It's usefulin generic programming cases when a given object may be either a regularclass or aRebindable.
Parameters:Rebindable!Tobj | 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 rebindablec2 = c2.rebindable;writeln(c2.payload);// 1 template
UnqualRef(T) if (is(T == class) || is(T == interface))
Similar toRebindable!(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 {}staticshared(Data) a;staticUnqualRef!(shared Data) b;import core.thread;auto thread =new core.thread.Thread({ a =newshared Data(); b =newshared Data();});thread.start();thread.join();assert(a !isnull);assert(bisnull); 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 astruct 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 astruct 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 denotesthe absence of a value. If default constructed, aNullable!T object starts in the null state. Assigning it renders itnon-null. Callingnullify can nullify it again.
Practically
Nullable!T stores a
T and a
bool.
See also:
apply, an alternative way to use the payload.
Examples:struct CustomerRecord{ string name; string address;int customerNum;}Nullable!CustomerRecord getByName(string name){//A bunch of hairy stuffreturnNullable!CustomerRecord.init;}auto queryResult = getByName("Doe, John");if (!queryResult.isNull){//Process Mr. Doe's customer recordauto 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);// 42a.nullify();assert(a.isNull);assertThrown!Throwable(a.get);
Examples:import std.algorithm.iteration : each, joiner;Nullable!int a = 42;Nullable!int b;// Add each value to an arrayint[] arr;a.each!((n) => arr ~= n);writeln(arr);// [42]b.each!((n) => arr ~= n);writeln(arr);// [42]// Take first value from an array of NullablesNullable!int[] c =newNullable!int[](10);c[7] =Nullable!int(42);writeln(c.joiner.front);// 42
this(inout T
value) inout;
Constructor initializingthis withvalue.
Parameters:Tvalue | The value to initialize thisNullable with. |
bool
opEquals(this This, Rhs)(auto ref Rhs
rhs)
if (!is(CommonType!(This, Rhs) == void));
bool
opEquals(this This, Rhs)(auto ref Rhs
rhs)
if (is(CommonType!(This, Rhs) == void) && is(typeof(this.get ==rhs)));
If they are both null, then they are equal. If one is null and the other is not, then they are not equal. If they are both non-null, then they are equal if their values are equal.
Examples:Nullable!int empty;Nullable!int a = 42;Nullable!int b = 42;Nullable!int c = 27;writeln(empty);// emptywriteln(empty);// Nullable!int.initassert(empty != a);assert(empty != b);assert(empty != c);writeln(a);// bassert(a != c);assert(empty != 42);writeln(a);// 42assert(c != 42);
string
toString();
string
toString() const;
void
toString(W)(ref W
writer, ref scope const FormatSpec!char
fmt)
if (isOutputRange!(W, char));
void
toString(W)(ref W
writer, ref scope const FormatSpec!char
fmt) const
if (isOutputRange!(W, char));
Gives the string
"Nullable.null" if
isNull is
true. Otherwise, the result is equivalent to calling
std.format.formattedWrite on the underlying value.
Returns:Astring ifwriter andfmt are not set;void otherwise.
pure nothrow @property @safe bool
isNull() const;
Check ifthis is in the null state.
Returns:trueiffthis is in the null state, otherwise false.
Examples:Nullable!int ni;assert(ni.isNull);ni = 0;assert(!ni.isNull);
bool
opCast(T : bool)() const;
Returns true ifthis has a value, otherwise false.
Allows a
Nullable to be used as the condition in an
if statement:
if (auto result = functionReturningNullable()){ doSomethingWith(result.get);}ref T
opCast(T, this This)()
if (is(This : T) || This.sizeof == T.sizeof);
PreventsopCast from disabling built-in conversions.
Forcesthis to the null state.
Examples:Nullable!int ni = 0;assert(!ni.isNull);ni.nullify();assert(ni.isNull);
ref Nullable
opAssign()(T
value) return;
Assignsvalue to the internally-held state. If the assignment succeeds,this becomes non-null.
Parameters:Tvalue | A value of typeT 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 this
Nullable is no different than assigning any other value of type
T, and the resulting code will look very strange. It is strongly recommended that this be avoided by instead using the version of
Nullable that takes an additional
nullValue template argument.
//PassesNullable!(int*) npi;assert(npi.isNull);//Passes?!npi =null;assert(!npi.isNull);
pure nothrow @property ref @safe inout(T)
get() inout;
@property inout(T)
get()(inout(T)
fallback) inout;
@property auto
get(U)(inout(U)
fallback) inout;
Gets the value if not null. Ifthis 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 theNullable is null. |
Returns:The value held internally by thisNullable.
alias
empty = isNull;
alias
popFront = nullify;
alias
popBack = nullify;
pure nothrow @property ref @safe inout(T)
front() inout;
alias
back = front;
@property inout(typeof(this))
save() inout;
inout(typeof(this))
opIndex(size_t[2]
dim) inout;
size_t[2]
opSlice(size_t dim : 0)(size_t
from, size_t
to) const;
pure nothrow @property @safe size_t
length() const;
template
opDollar(size_t dim : 0)
pure nothrow ref @safe inout(T)
opIndex(size_t
index) inout;
auto
opSlice(this This)();
ConvertsNullable to a range. Works even when the contained type isimmutable.
struct
Nullable(T, T nullValue);
auto
nullable(alias nullValue, T)(T
t)
if (is(typeof(nullValue) == T));
Just likeNullable!T, except that the null state is defined as aparticular value. For example,Nullable!(uint, uint.max) is anuint that sets aside the valueuint.max to denote a nullstate.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 thisNullable. Must be of typeT. |
Examples:Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle){//Find the needle, returning -1 if not foundreturnNullable!(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 overheadstaticassert(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);// 5staticassert(a.sizeof ==int.sizeof);
Examples:auto a =nullable!(int.min)(8);writeln(a);// 8a.nullify();assert(a.isNull);
Constructor initializingthis withvalue.
Parameters:Tvalue | The value to initialize thisNullable with. |
@property bool
isNull() const;
Check ifthis is in the null state.
Returns:trueiffthis is in the null state, otherwise false.
Examples:Nullable!(int, -1) ni;//Initialized to "null" stateassert(ni.isNull);ni = 0;assert(!ni.isNull);
Forcesthis to the null state.
Examples:Nullable!(int, -1) ni = 0;assert(!ni.isNull);ni = -1;assert(ni.isNull);
Assignsvalue to the internally-held state. If the assignmentsucceeds,this becomes non-null. No null checks are made. Notethat the assignment may leavethis in the null state.
Parameters:Tvalue | A value of typeT 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 for
nullValue, then assigning the null value to this
Nullable is no different than assigning any other value of type
T, and the resulting code will look very strange. It is strongly recommended that this be avoided by using
T's "built in" null value for
nullValue.
//Passesenum nullVal =cast(int*) 0xCAFEBABE;Nullable!(int*, nullVal) npi;assert(npi.isNull);//Passes?!npi =null;assert(!npi.isNull);
@property ref inout(T)
get() inout;
Gets the value.this must not be in the null state.This function is also called for the implicit conversion toT.
PreconditionsisNull must befalse.
Returns:The value held internally by thisNullable.
Examples:import std.exception : assertThrown, assertNotThrown;Nullable!(int, -1) ni;//`get` is implicitly called. Will throw//an error in non-release modeassertThrown!Throwable(ni == 0);ni = 0;assertNotThrown!Throwable(ni == 0);
Unpacks the content of aNullable, performs an operation and packs it again. Does nothing if isNull.
When called on aNullable,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 | aNullable |
| fun | a function operating on the content of the nullable |
Returns:fun(t.get).nullable if
!t.isNull, else
Nullable.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 likeNullable!T, except that the object refers to a valuesitting elsewhere in memory. This makes assignments overwrite theinitially assigned value. InternallyNullableRef!T only stores apointer 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);// 5writeln(x);// 5a = 42;writeln(x);// 42assert(!a.isNull);writeln(a);// 42a.nullify();writeln(x);// 42assert(a.isNull);assertThrown!Throwable(a.get);assertThrown!Throwable(a = 71);a.bind(&y);writeln(a);// 7y = 135;writeln(a);// 135
pure nothrow @safe this(T*
value);
Constructor bindingthis tovalue.
Parameters:T*value | The value to bind to. |
pure nothrow @safe void
bind(T*
value);
Binds the internal state tovalue.
Parameters:T*value | A pointer to a value of typeT to bind thisNullableRef to. |
Examples:NullableRef!int nr =newint(42);writeln(nr);// 42int* n =newint(1);nr.bind(n);writeln(nr);// 1
pure nothrow @property @safe bool
isNull() const;
Returnstrue if and only ifthis is in the null state.
Returns:true ifthis is in the null state, otherwise false.
Examples:NullableRef!int nr;assert(nr.isNull);int* n =newint(42);nr.bind(n);assert(!nr.isNull && nr == 42);
pure nothrow @safe void
nullify();
Forcesthis to the null state.
Examples:NullableRef!int nr =newint(42);assert(!nr.isNull);nr.nullify();assert(nr.isNull);
void
opAssign()(T
value)
if (isAssignable!T);
Assignsvalue to the internally-held state.
Parameters:Tvalue | A value of typeT 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(newint(0));assert(!nr.isNull);assertNotThrown!Throwable(nr = 42);writeln(nr);// 42
pure nothrow @property ref @safe inout(T)
get() inout;
Gets the value.this must not be in the null state.This function is also called for the implicit conversion toT.
Examples:import std.exception : assertThrown, assertNotThrown;NullableRef!int nr;//`get` is implicitly called. Will throw//an error in non-release modeassertThrown!Throwable(nr == 0);nr.bind(newint(0));assertNotThrown!Throwable(nr == 0);
BlackHole!Base is a subclass ofBase which automatically implementsall abstract member functions inBase as do-nothing functions. Eachauto-implemented function just returns the default value of the return typewithout doing anything.
Parameters:| Base | A non-final class forBlackHole to inherit from. |
Examples:import std.math.traits : isNaN;staticabstractclass C{int m_value;this(int v) { m_value = v; }int value() @property {return m_value; }abstractreal realValue() @property;abstractvoid doSomething();}auto c =newBlackHole!C(42);writeln(c.value);// 42// Returns real.init which is NaNassert(c.realValue.isNaN);// Abstract functions are implemented as do-nothingc.doSomething(); template
WhiteHole(Base)
class
NotImplementedError:
object.Error;
WhiteHole!Base is a subclass ofBase which automatically implementsall abstract member functions as functions that always fail. These functionssimply throw anError and never return.Whitehole is useful fortrapping the use of class member functions that haven't been implemented.
Parameters:| Base | A non-final class forWhiteHole to inherit from. |
Examples:import std.exception : assertThrown;staticclass C{abstractvoid notYetImplemented();}auto c =newWhiteHole!C;assertThrown!NotImplementedError(c.notYetImplemented());// throws an Error Examples:import std.exception : assertThrown;// nothrow{interface I_1 {void foo();void bar()nothrow; }auto o =newWhiteHole!I_1; assertThrown!NotImplementedError(o.foo()); assertThrown!NotImplementedError(o.bar());}// doc example{staticclass C {abstractvoid notYetImplemented(); }auto c =newWhiteHole!C;try { c.notYetImplemented();assert(0); }catch (Error e) {}} class
AutoImplement(Base, alias how, alias what = isAbstractFunction) if (!is(how == class)): Base;
class
AutoImplement(Interface, BaseClass, alias how, alias what = isAbstractFunction) if (is(Interface == interface) && is(BaseClass == class)): BaseClass, Interface;
AutoImplement automatically implements (by default) all abstract memberfunctions in the class or interfaceBase in specified way.
The second version ofAutoImplement automatically implementsInterface, while deriving fromBaseClass.
Parameters:| how | template which specifies how functions will be implemented/overridden. Two arguments are passed tohow: 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).
You may want to use templated property functions (instead of Implicit Template Properties) to generate complex functions:// Prints log messages for each call to overridden functions.string generateLogger(C,alias fun)() @property{import std.traits;enum qname = C.stringof ~"." ~__traits(identifier, fun); string stmt; stmt ~=q{ struct Importer { import std.stdio; } }; stmt ~=`Importer.writeln("Log: ` ~ qname ~`(", args, ")");`;staticif (!__traits(isAbstractFunction, fun)) {staticif (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 towhat: 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.enumbool hasValue(alias fun) = !is(ReturnType!(fun) ==void); |
NoteGenerated code is inserted in the scope ofstd.typecons module. Thus,any useful functions outsidestd.typecons cannot be used in the generatedcode. To workaround this problem, you mayimport necessary things in alocal struct, as done in thegenerateLogger() template in the aboveexample.
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]
- Theparent keyword is actually a delegate to the super class' corresponding member function. [Bugzilla 2540]
- Using alias template parameter inhow 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();}staticabstractclass AbstractFallbackPackageSupplier : PackageSupplier{protected PackageSupplier default_, fallback;this(PackageSupplier default_, PackageSupplier fallback) {this.default_ = default_;this.fallback = fallback; }abstractint foo();abstractint bar();}template fallback(T,alias func){import std.format : format;// for all implemented methods:// - try default first// - only on a failure run & return fallbackenum fallback =q{ try { return default_.%1$s(args); } catch (Exception) { return fallback.%1$s(args); } }.format(__traits(identifier, func));}// combines two classes and use the second one as fallbackalias FallbackPackageSupplier =AutoImplement!(AbstractFallbackPackageSupplier, fallback);class FailingPackageSupplier : PackageSupplier{int foo(){thrownew 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());// -1writeln(registry.bar());// 2 template
generateEmptyFunction(C, func...)
enum string
generateAssertTrap(C, func...);
Predefined how-policies forAutoImplement. 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 anythingwriteln(i.foo);// 0assert(i.barisnull); 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 interfaceassertThrown!NotImplementedError(i.foo);assertThrown!NotImplementedError(i.bar); template
wrap(Targets...) if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
template
wrap(Targets...) if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))
template
unwrap(Target) if (isMutable!Target)
template
unwrap(Target) if (!isMutable!Target)
Supports structural based typesafe conversion.
If
Source has structural conformance with the
interfaceTargets,wrap creates an internal wrapper class which inherits
Targets andwraps the
src object, then returns it.
unwrap can be used to extract objects which have been wrapped by
wrap.
Examples:interface Quack{int quack(); @propertyint height();}interface Flyer{ @propertyint height();}class Duck : Quack{int quack() {return 1; } @propertyint height() {return 10; }}class Human{int quack() {return 2; } @propertyint height() {return 20; }}Duck d1 =new Duck();Human h1 =new Human();interface Refleshable{int reflesh();}// does not have structural conformancestaticassert(!__traits(compiles, d1.wrap!Refleshable));staticassert(!__traits(compiles, h1.wrap!Refleshable));// strict upcastQuack qd = d1.wrap!Quack;assert(qdis d1);assert(qd.quack() == 1);// calls Duck.quack// strict downcastDuck d2 = qd.unwrap!Duck;assert(d2is d1);// structural upcastQuack qh = h1.wrap!Quack;assert(qh.quack() == 2);// calls Human.quack// structural downcastHuman h2 = qh.unwrap!Human;assert(h2is h1);// structural upcast (two steps)Quack qx = h1.wrap!Quack;// Human -> QuackFlyer fx = qx.wrap!Flyer;// Quack -> Flyerassert(fx.height == 20);// calls Human.height// structural downcast (two steps)Quack qy = fx.unwrap!Quack;// Flyer -> QuackHuman hy = qy.unwrap!Human;// Quack -> Humanassert(hyis h1);// structural downcast (one step)Human hz = fx.unwrap!Human;// Flyer -> Humanassert(hzis h1); Examples:import std.traits : FunctionAttribute, functionAttributes;interface A {int run(); }interface B {int stop(); @propertyint status(); }class X{int run() {return 1; }int stop() {return 2; } @propertyint status() {return 3; }}auto x =new X();auto ab = x.wrap!(A, B);A a = ab;B b = ab;writeln(a.run());// 1writeln(b.stop());// 2writeln(b.status);// 3staticassert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property); enum
RefCountedAutoInitialize: int;
Options regarding auto-initialization of aSafeRefCounted object (seethe definition ofSafeRefCounted below).
Examples:import core.exception : AssertError;import std.exception : assertThrown;struct Foo{int a = 42;}SafeRefCounted!(Foo,RefCountedAutoInitialize.yes) rcAuto;SafeRefCounted!(Foo,RefCountedAutoInitialize.no) rcNoAuto;writeln(rcAuto.refCountedPayload.a);// 42assertThrown!AssertError(rcNoAuto.refCountedPayload);rcNoAuto.refCountedStore.ensureInitialized;writeln(rcNoAuto.refCountedPayload.a);// 42 Do not auto-initialize the object
Auto-initialize the object
struct
SafeRefCounted(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes) if (!is(T == class) && !is(T == interface));
Defines a reference-counted object containing aT value aspayload.
An instance of
SafeRefCounted is a reference to a structure,which is referred to as the
store, or
storage implementationstruct in this documentation. The store contains a reference countand the
T payload.
SafeRefCounted uses
malloc to allocatethe store. As instances of
SafeRefCounted are copied or go out ofscope, they will automatically increment or decrement the referencecount. When the reference count goes down to zero,
SafeRefCountedwill call
destroy against the payload and call
free todeallocate the store. If the
T payload contains any referencesto GC-allocated memory, then
SafeRefCounted will add it to the GC memorythat is scanned for pointers, and remove it from GC scanning before
free is called on the store.
One important consequence of
destroy is that it will call thedestructor of the
T payload. GC-managed references are notguaranteed to be valid during a destructor call, but other members of
T, such as file handles or pointers to
malloc memory, willstill be valid during the destructor call. This allows the
T todeallocate or clean up any non-GC resources immediately after thereference count has reached zero.
Without -preview=dip1000,
SafeRefCounted is unsafe and should beused with care. No references to the payload should be escaped outsidethe
SafeRefCounted object.
With -preview=dip1000,
SafeRefCounted is safe if it's payload is accessed onlywith the
borrow function. Scope semantics can also prevent accidentalescaping of
refCountedPayload, but it's still up to the user to not destroythe last counted reference while the payload is in use. Due to that,
refCountedPayload remains accessible only in
@system code.
The
autoInit option makes the object ensure the store isautomatically initialized. Leaving
autoInit ==RefCountedAutoInitialize.yes (the default option) is convenient buthas the cost of a test whenever the payload is accessed. If
autoInit == RefCountedAutoInitialize.no, user code must call either
refCountedStore.isInitialized or
refCountedStore.ensureInitializedbefore attempting to access the payload. Not doing so results in nullpointer dereference.
If
T.this() is annotated with
@disable then
autoInit must be
RefCountedAutoInitialize.no in order to compile.
Examples:// A pair of an `int` and a `size_t` - the latter being the// reference count - will be dynamically allocatedauto rc1 =SafeRefCounted!int(5);writeln(rc1);// 5// No more allocation, add just one extra reference countauto rc2 = rc1;// Reference semanticsrc2 = 42;writeln(rc1);// 42// the pair will be freed when rc1 and rc2 go out of scope
SafeRefCounted storage implementation.
pure nothrow @nogc @property @safe bool
isInitialized() const;
Returnstrue if and only if the underlying store has been allocated and initialized.
pure nothrow @nogc @property @safe size_t
refCount() const;
Returns underlying reference count if it is allocated and initialized (a positive integer), and0 otherwise.
pure nothrow @safe void
ensureInitialized()();
Makes sure the payload was properly initialized. Such a call is typically inserted before using the payload.
This function is unavailable ifT.this() is annotated with@disable.
nothrow @property ref @safe inout(RefCountedStore)
refCountedStore() inout;
Returns storage implementation struct.
this
(A...)(auto ref A
args)
if (A.length > 0);
this(return scope T
val);
Constructor that initializes the payload.
PostconditionrefCountedStore.isInitialized
void
opAssign(typeof(this)
rhs);
void
opAssign(T
rhs);
Assignment operators.
NoteYou may not assign a new payload to an uninitialized SafeRefCounted, ifauto initialization is off. Assigning another counted reference is still okay.
@property ref @system T
refCountedPayload() return;
pure nothrow @nogc @property ref @system inout(T)
refCountedPayload() inout return;
Returns a reference to the payload. If (autoInit == RefCountedAutoInitialize.yes), calls refCountedStore.ensureInitialized. Otherwise, just issues assert(refCountedStore.isInitialized). Used withalias refCountedPayload this;, so callers can just use theSafeRefCounted object as aT.
The first overload exists only ifautoInit == RefCountedAutoInitialize.yes. So ifautoInit == RefCountedAutoInitialize.no or called for a constant or immutable object, thenrefCountedPayload will also be qualified as nothrow (but will still assert if not initialized).
template
borrow(alias fun)
Borrows the payload of
SafeRefCounted for use in
fun. Inferred as
@safeif
fun is
@safe and does not escape a reference to the payload.The reference count will be incremented for the duration of the operation,so destroying the last reference will not leave dangling references in
fun.
Parameters:| fun | A callable accepting the payload either by value or by reference. |
| RC refCount | The counted reference to the payload. |
Returns:The return value offun, if any.ref in the return value will be forwarded.
IssuesFor yet unknown reason, code that uses this function with UFCS syntax will not be inferred as@safe. It will still compile if the code is explicitly marked@safe and nothing infun prevents that.
Examples:This example can be marked
@safe with
-preview=dip1000.
auto rcInt = safeRefCounted(5);writeln(rcInt.borrow!(theInt => theInt));// 5auto sameInt = rcInt;writeln(sameInt.borrow!"a");// 5// using `ref` in the functionauto arr = [0, 1, 2, 3, 4, 5, 6];sameInt.borrow!(ref (x) => arr[x]) = 10;writeln(arr);// [0, 1, 2, 3, 4, 10, 6]// modifying the payload via an aliassameInt.borrow!"a*=2";writeln(rcInt.borrow!"a");// 10
SafeRefCounted!(T, RefCountedAutoInitialize.no)
safeRefCounted(T)(T
val);
Initializes aSafeRefCounted withval. The template parameterT ofSafeRefCounted is inferred fromval. This function can be used to move non-copyable values to the heap. It also disables theautoInit option ofSafeRefCounted.
Parameters:Tval | The value to be reference counted |
Returns:An initializedSafeRefCounted containingval.
Examples:staticstruct File{static size_t nDestroyed; string name; @disablethis(this);// not copyable ~this() { name =null; ++nDestroyed; }}auto file = File("name");writeln(file.name);// "name"// file cannot be copied and has unique ownershipstaticassert(!__traits(compiles, {auto file2 = file;}));writeln(File.nDestroyed);// 0// make the file ref counted to share ownership// Note:// We write a compound statement (brace-delimited scope) in which all `SafeRefCounted!File` handles are created and deleted.// This allows us to see (after the scope) what happens after all handles have been destroyed.{// We move the content of `file` to a separate (and heap-allocated) `File` object,// managed-and-accessed via one-or-multiple (initially: one) `SafeRefCounted!File` objects ("handles").// This "moving":// (1) invokes `file`'s destructor (=> `File.nDestroyed` is incremented from 0 to 1 and `file.name` becomes `null`);// (2) overwrites `file` with `File.init` (=> `file.name` becomes `null`).// It appears that writing `name = null;` in the destructor is redundant,// but please note that (2) is only performed if `File` defines a destructor (or post-blit operator),// and in the absence of the `nDestroyed` instrumentation there would have been no reason to define a destructor.import std.algorithm.mutation : move;auto rcFile =safeRefCounted(move(file)); writeln(rcFile.name);// "name" writeln(File.nDestroyed);// 1 writeln(file.name);// null// We create another `SafeRefCounted!File` handle to the same separate `File` object.// While any of the handles is still alive, the `File` object is kept alive (=> `File.nDestroyed` is not modified).auto rcFile2 = rcFile; writeln(rcFile.refCountedStore.refCount);// 2 writeln(File.nDestroyed);// 1}// The separate `File` object is deleted when the last `SafeRefCounted!File` handle is destroyed// (i.e. at the closing brace of the compound statement above, which destroys both handles: `rcFile` and `rcFile2`)// (=> `File.nDestroyed` is incremented again, from 1 to 2):writeln(File.nDestroyed);// 2 Creates a proxy for the valuea that will forward all operations while disabling implicit conversions. The aliased itema must be anlvalue. This is useful for creating a new type from the "base" type (though this isnot 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{privateint value;mixinProxy!value;this(int n){ value = n; }}MyInt n = 10;// Enable operations that original type has.++n;writeln(n);// 11writeln(n * 2);// 22void 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 lvalueint n;mixinProxy!n;this(int n) {this.n = n; }}NewIntType nit = 0;nit++;writeln(nit);// 1struct NewObjectType{ Object obj;//Ok, obj is an lvaluemixinProxy!obj;this (Object o) { obj = o; }}NewObjectType not =new Object();assert(__traits(compiles, not.toHash())); Examples:There is one exception to the fact that the new type is not related to the old type.
Pseudo-member functions are usable with the new type; they will be forwarded on to the proxied value.
import std.math.traits : isInfinity;float f = 1.0;assert(!f.isInfinity);struct NewFloat{float _;mixinProxy!_;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 isbased on an existing type. Unlike thealias 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 typeT |
NoteIf a library routine cannot handle the Typedef type,you can use theTypedefType template to extract thetype 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);staticassert(is(TypedefType!MyIntInit ==int));staticassert(MyIntInit() == 42);
Examples:Typedef creates a new type
alias MyInt =Typedef!int;staticvoid takeInt(int) {}staticvoid takeMyInt(MyInt) {}int i;takeInt(i);// okstaticassert(!__traits(compiles, takeMyInt(i)));MyInt myInt;staticassert(!__traits(compiles, takeInt(myInt)));takeMyInt(myInt);// ok Examples:Use the optional
cookie argument to create different types of the same base type
alias TypeInt1 =Typedef!int;alias TypeInt2 =Typedef!int;// The two Typedefs are the same type.staticassert(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.staticassert(!is(MoneyEuros == MoneyDollars));
string
toString(this T)();
void
toString(this T, W)(ref W
writer, ref scope const FormatSpec!char
fmt)
if (isOutputRange!(W, char));
Convert wrapped value to a human readable string
Examples:import std.conv : to;int i = 123;auto td = Typedef!int(i);writeln(i.to!string);// td.to!string
Get the underlying type which aTypedef wraps.IfT is not aTypedef it will alias itself toT.
Examples:import std.conv : to;alias MyInt = Typedef!int;staticassert(is(TypedefType!MyInt ==int));/// Instantiating with a non-Typedef will return that typestaticassert(is(TypedefType!int ==int));string num ="5";// extract the needed typeMyInt myInt = MyInt( num.to!(TypedefType!MyInt) );writeln(myInt);// 5// cast to the underlying type to get the value that's being wrappedint x =cast(TypedefType!MyInt) myInt;alias MyIntInit = Typedef!(int, 42);staticassert(is(TypedefType!MyIntInit ==int));staticassert(MyIntInit() == 42);
template
scoped(T) if (is(T == class))
Allocates aclass 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 theobject outside the scope.
The class destructor will be called when the result of
scoped() isitself destroyed.
Scoped class instances can be embedded in a parent
class or
struct,just like a child struct instance. Scoped member variables must havetype
typeof(scoped!Class(args)), and be initialized with a call toscoped. See below for an example.
NoteIt's illegal to move a class instance even if you are sure thereare 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 stackauto a1 =scoped!A();a1.x = 42;// Result of `scoped` call implicitly converts to a class referenceA 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();}// Restrictionsversion (Bug){import std.algorithm.mutation : move;auto invalid = a1.move;// illegal, scoped objects can't be moved}staticassert(!is(typeof({auto e1 = a1;// illegal, scoped objects can't be copiedassert([a1][0].x == 42);// ditto})));staticassert(!is(typeof({alias ScopedObject =typeof(a1);auto e2 = ScopedObject();// illegal, must be built via scoped!Aauto e3 = ScopedObject(1);// ditto})));// Use with aliasalias makeScopedA =scoped!A;auto a3 = makeScopedA();auto a4 = makeScopedA(1);// Use as member variablestruct B{typeof(scoped!A()) a;// note the trailing parenthesesthis(int i) {// construct member a =scoped!A(i); }}// Stack-allocateauto b1 = B(5);aRef = b1.a;writeln(aRef.x);// 5destroy(b1);// calls A's destructor for b1.a// aRef is now an invalid reference// Heap-allocateauto b2 =new B(6);writeln(b2.a.x);// 6destroy(*b2);// calls A's destructor for b2.a @system auto
scoped(Args...)(auto ref Args
args);
Returns the scoped object.
Parameters:Argsargs | Arguments to pass toT's constructor. |
template
Flag(string name)
Defines a simple, self-documenting yes/no flag. This makes it easy forAPIs to define functions accepting flags without resorting tobool, which is opaque in calls, and without needing to define anenumerated type separately. UsingFlag!"Name" instead ofbool makes the flag's meaning visible in calls. Each yes/no flag hasits own type, which makes confusions and mix-ups impossible.
ExampleCode callinggetLine (usually far away from its definition) can't beunderstood without looking at the documentation, even by users familiar withthe API:
string getLine(bool keepTerminator){ ...if (keepTerminator) ... ...}...auto line = getLine(false);Assuming the reverse meaning (i.e. "ignoreTerminator") and inserting the wrongcode compiles and runs with erroneous results.
After replacing the boolean parameter with an instantiation of
Flag, codecalling
getLine can be easily read and understood even by people notfluent with the API:
string getLine(Flag!"keepTerminator" keepTerminator){ ...if (keepTerminator) ... ...}...auto line = getLine(Yes.keepTerminator);The structs
Yes and
No are provided as shorthand for
Flag!"Name".yes and
Flag!"Name".no and are preferred for brevity andreadability. These convenience structs mean it is usually unnecessary andcounterproductive to create an alias of a
Flag as a way of avoiding typingout the full type while specifying the affirmative or negative options.
Passing categorical data by means of unstructured
boolparameters is classified under "simple-data coupling" by SteveMcConnell in the
Code Complete book, along with three otherkinds of coupling. The author argues citing several studies thatcoupling has a negative effect on code quality.
Flag offers asimple structuring method for passing yes/no flags to APIs.
Examples:Flag!"abc" flag;writeln(flag);// Flag!"abc".nowriteln(flag);// No.abcassert(!flag);if (flag)assert(0);
Examples:auto flag = Yes.abc;assert(flag);writeln(flag);// Yes.abcif (!flag)assert(0);if (flag) {}elseassert(0); When creating a value of typeFlag!"Name", use Flag!"Name".no for the negative option. When using a value of typeFlag!"Name", compare it against Flag!"Name".no or justfalse or0.
When creating a value of typeFlag!"Name", use Flag!"Name".yes for the affirmative option. When using a value of typeFlag!"Name", compare it against Flag!"Name".yes.
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".nowriteln(flag);// No.abcassert(!flag);if (flag)assert(0);
Examples:auto flag =Yes.abc;assert(flag);writeln(flag);// Yes.abcif (!flag)assert(0);if (flag) {}elseassert(0); enum auto
opDispatch(string name);
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 includinga "None" value.
Examples:enum A{ None, A = 1 << 0, B = 1 << 1, C = 1 << 2, D = 1 << 3,}staticassert(isBitFlagEnum!A); Examples:Test an enum with default (consecutive) values
enum B{ A, B, C, D// D == 3}staticassert(!isBitFlagEnum!B); Examples:Test an enum with non-integral values
enum C:double{ A = 1 << 0, B = 1 << 1}staticassert(!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 ofenum values. It can be used if all the enum values are integral constants witha bit count of at most 1, or if the
unsafe parameter is explicitly set toYes.This is much safer than using the enum itself to storethe 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 SwitchErrorfinalswitch (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 setimmutableBitFlags!Enum flags_empty;assert(!flags_empty.A);// Value can be set with the | operatorimmutable flags_A = flags_empty | Enum.A;// and tested using property accessassert(flags_A.A);// or the & operatorassert(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}immutableBitFlags!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,}immutableBitFlags!Enum flags_AB =BitFlags!Enum(Enum.A, Enum.B);immutableBitFlags!Enum flags_BC =BitFlags!Enum(Enum.B, Enum.C);// Use the ~ operator for subtracting flagsimmutableBitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A);assert(!flags_B.A && flags_B.B && !flags_B.C);// use & between BitFlags for intersectionwriteln(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 falseassert(!flags);// BitFlags with at least one value set evaluate to trueflags |= Enum.A;assert(flags);// This can be useful to check intersection between BitFlagsBitFlags!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 flagsauto value =cast(int) flags;writeln(value);// Enum.A Examples:You need to specify the
unsafe parameter for enums with custom values
enum UnsafeEnum{ A = 1, B = 2, C = 4, BC = B|C}staticassert(!__traits(compiles, {BitFlags!UnsafeEnum flags; }));BitFlags!(UnsafeEnum, Yes.unsafe) flags;// property access tests for exact match of unsafe enumsflags.B =true;assert(!flags.BC);// only Bflags.C =true;assert(flags.BC);// both B and Cflags.B =false;assert(!flags.BC);// only C// property access sets all bits of unsafe enum groupflags = 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 ofFrom intoTo, in one or more typesT. Forexample,ReplaceType!(int, uint, Tuple!(int, float)[string]) yieldsTuple!(uint, float)[string]. The types in which replacement is performedmay be arbitrarily complex, including qualifiers, built-in type constructors(pointers, arrays, associative arrays, functions, and delegates), and templateinstantiations; replacement proceeds transitively through the type definition.However, member types instructs orclasses are not replaced because thereare no ways to express the types resulting after replacement.
This is an advanced type manipulation necessary e.g. for replacing theplaceholder type
This in
std.variant.Algebraic.
Returns:ReplaceType aliases itself to the type(s) that result afterreplacement.
Examples:staticassert(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 which
pred evaluates to
true.
Examples:import std.traits : isArray;staticassert(is(ReplaceTypeUnless!(isArray,int, string,int*) == string*) &&is(ReplaceTypeUnless!(isArray,int, string,int[]) ==int[]) &&is(ReplaceTypeUnless!(isArray,int, string, Tuple!(int,int[])) == Tuple!(string,int[])));
Ternary type with three truth values:
Ternary.yes fortrueTernary.no forfalseTernary.unknown as an unknown state
Also known as trinary, trivalent, or trilean.
Examples:Ternary a;writeln(a);// Ternary.unknownwriteln(~Ternary.yes);// Ternary.nowriteln(~Ternary.no);// Ternary.yeswriteln(~Ternary.unknown);// Ternary.unknown
enum Ternary
no;
enum Ternary
yes;
enum Ternary
unknown;
The possible states of theTernary
pure nothrow @nogc @safe this(bool
b);
pure nothrow @nogc @safe void
opAssign(bool
b);
Construct and assign from abool, receivingno forfalse andyes fortrue.
pure nothrow @nogc @safe this(const Ternary
b);
Construct a ternary value from another ternary value
Ternary
opUnary(string s)()
if (s == "~");
Ternary
opBinary(string s)(Ternary
rhs)
if (s == "|");
Ternary
opBinary(string s)(Ternary
rhs)
if (s == "&");
Ternary
opBinary(string s)(Ternary
rhs)
if (s == "^");
Ternary
opBinary(string s)(bool
rhs)
if (s == "|" || s == "&" || s == "^");
Truth table for logical operations| a | b | ˜a | a | b | a & b | a ^ b |
|---|
| no | no | yes | no | no | no |
| no | yes | | yes | no | yes |
| no | unknown | | unknown | no | unknown |
| yes | no | no | yes | no | yes |
| yes | yes | | yes | yes | no |
| yes | unknown | | yes | unknown | unknown |
| unknown | no | unknown | unknown | no | unknown |
| unknown | yes | | yes | unknown | unknown |
| unknown | unknown | | unknown | unknown | unknown |
struct
RefCounted(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes);
The old version of
SafeRefCounted, before
borrow existed.Old code may be relying on
@safety of some of the member functions whichcannot be safe in the new scheme, andcan avoid breakage by continuing to use this.
SafeRefCounted should bepreferred, as this type is outdated and unrecommended for new code.
Examples:auto rc1 =RefCounted!int(5);writeln(rc1);// 5auto rc2 = rc1;rc2 = 42;writeln(rc1);// 42
RefCounted!(T, RefCountedAutoInitialize.no)
refCounted(T)(T
val);
Like
safeRefCounted but used to initialize
RefCounted instead. Intended for backwards compatibility, otherwise it is preferable to use
safeRefCounted.
Examples:staticstruct File{static size_t nDestroyed; string name; @disablethis(this);// not copyable ~this() { name =null; ++nDestroyed; }}auto file = File("name");writeln(file.name);// "name"staticassert(!__traits(compiles, {auto file2 = file;}));writeln(File.nDestroyed);// 0{import std.algorithm.mutation : move;auto rcFile =refCounted(move(file)); writeln(rcFile.name);// "name" writeln(File.nDestroyed);// 1 writeln(file.name);// nullauto rcFile2 = rcFile; writeln(rcFile.refCountedStore.refCount);// 2 writeln(File.nDestroyed);// 1}writeln(File.nDestroyed);// 2