1760c2415Smrg // Written in the D programming language.
2760c2415Smrg
3760c2415Smrg /**
4760c2415Smrg This module implements a variety of type constructors, i.e., templates
5760c2415Smrg that allow construction of new, useful general-purpose types.
6760c2415Smrg
7760c2415Smrg $(SCRIPT inhibitQuickIndex = 1;)
8760c2415Smrg $(BOOKTABLE,
9760c2415Smrg $(TR $(TH Category) $(TH Functions))
10760c2415Smrg $(TR $(TD Tuple) $(TD
11760c2415Smrg $(LREF isTuple)
12760c2415Smrg $(LREF Tuple)
13760c2415Smrg $(LREF tuple)
14760c2415Smrg $(LREF reverse)
15760c2415Smrg ))
16760c2415Smrg $(TR $(TD Flags) $(TD
17760c2415Smrg $(LREF BitFlags)
18760c2415Smrg $(LREF isBitFlagEnum)
19760c2415Smrg $(LREF Flag)
20760c2415Smrg $(LREF No)
21760c2415Smrg $(LREF Yes)
22760c2415Smrg ))
23760c2415Smrg $(TR $(TD Memory allocation) $(TD
24760c2415Smrg $(LREF RefCounted)
25760c2415Smrg $(LREF refCounted)
26760c2415Smrg $(LREF RefCountedAutoInitialize)
27760c2415Smrg $(LREF scoped)
28760c2415Smrg $(LREF Unique)
29760c2415Smrg ))
30760c2415Smrg $(TR $(TD Code generation) $(TD
31760c2415Smrg $(LREF AutoImplement)
32760c2415Smrg $(LREF BlackHole)
33760c2415Smrg $(LREF generateAssertTrap)
34760c2415Smrg $(LREF generateEmptyFunction)
35760c2415Smrg $(LREF WhiteHole)
36760c2415Smrg ))
37760c2415Smrg $(TR $(TD Nullable) $(TD
38760c2415Smrg $(LREF Nullable)
39760c2415Smrg $(LREF nullable)
40760c2415Smrg $(LREF NullableRef)
41760c2415Smrg $(LREF nullableRef)
42760c2415Smrg ))
43760c2415Smrg $(TR $(TD Proxies) $(TD
44760c2415Smrg $(LREF Proxy)
45760c2415Smrg $(LREF rebindable)
46760c2415Smrg $(LREF Rebindable)
47760c2415Smrg $(LREF ReplaceType)
48760c2415Smrg $(LREF unwrap)
49760c2415Smrg $(LREF wrap)
50760c2415Smrg ))
51760c2415Smrg $(TR $(TD Types) $(TD
52760c2415Smrg $(LREF alignForSize)
53760c2415Smrg $(LREF Ternary)
54760c2415Smrg $(LREF Typedef)
55760c2415Smrg $(LREF TypedefType)
56760c2415Smrg $(LREF UnqualRef)
57760c2415Smrg ))
58760c2415Smrg )
59760c2415Smrg
60760c2415Smrg Copyright: Copyright the respective authors, 2008-
61760c2415Smrg License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
62760c2415Smrg Source: $(PHOBOSSRC std/_typecons.d)
63760c2415Smrg Authors: $(HTTP erdani.org, Andrei Alexandrescu),
64760c2415Smrg $(HTTP bartoszmilewski.wordpress.com, Bartosz Milewski),
65760c2415Smrg Don Clugston,
66760c2415Smrg Shin Fujishiro,
67760c2415Smrg Kenji Hara
68760c2415Smrg */
69760c2415Smrg module std.typecons;
70760c2415Smrg
71760c2415Smrg import core.stdc.stdint : uintptr_t;
72760c2415Smrg import std.meta; // : AliasSeq, allSatisfy;
73760c2415Smrg import std.traits;
74760c2415Smrg
75760c2415Smrg ///
76760c2415Smrg @safe unittest
77760c2415Smrg {
78760c2415Smrg // value tuples
79760c2415Smrg alias Coord = Tuple!(int, "x", int, "y", int, "z");
80760c2415Smrg Coord c;
81760c2415Smrg c[1] = 1; // access by index
82760c2415Smrg c.z = 1; // access by given name
83760c2415Smrg assert(c == Coord(0, 1, 1));
84760c2415Smrg
85760c2415Smrg // names can be omitted
86760c2415Smrg alias DicEntry = Tuple!(string, string);
87760c2415Smrg
88760c2415Smrg // tuples can also be constructed on instantiation
89760c2415Smrg assert(tuple(2, 3, 4)[1] == 3);
90760c2415Smrg // construction on instantiation works with names too
91760c2415Smrg assert(tuple!("x", "y", "z")(2, 3, 4).y == 3);
92760c2415Smrg
93760c2415Smrg // Rebindable references to const and immutable objects
94760c2415Smrg {
foo()95760c2415Smrg class Widget { void foo() const @safe {} }
96760c2415Smrg const w1 = new Widget, w2 = new Widget;
97760c2415Smrg w1.foo();
98760c2415Smrg // w1 = w2 would not work; can't rebind const object
99760c2415Smrg auto r = Rebindable!(const Widget)(w1);
100760c2415Smrg // invoke method as if r were a Widget object
101760c2415Smrg r.foo();
102760c2415Smrg // rebind r to refer to another object
103760c2415Smrg r = w2;
104760c2415Smrg }
105760c2415Smrg }
106760c2415Smrg
107760c2415Smrg debug(Unique) import std.stdio;
108760c2415Smrg
109760c2415Smrg /**
110760c2415Smrg Encapsulates unique ownership of a resource.
111760c2415Smrg
112760c2415Smrg When a $(D Unique!T) goes out of scope it will call $(D destroy)
113760c2415Smrg on the resource $(D T) that it manages, unless it is transferred.
114760c2415Smrg One important consequence of $(D destroy) is that it will call the
115760c2415Smrg destructor of the resource $(D T). GC-managed references are not
116760c2415Smrg guaranteed to be valid during a destructor call, but other members of
117760c2415Smrg $(D T), such as file handles or pointers to $(D malloc) memory, will
118760c2415Smrg still be valid during the destructor call. This allows the resource
119760c2415Smrg $(D T) to deallocate or clean up any non-GC resources.
120760c2415Smrg
121760c2415Smrg If it is desirable to persist a $(D Unique!T) outside of its original
122760c2415Smrg scope, then it can be transferred. The transfer can be explicit, by
123760c2415Smrg calling $(D release), or implicit, when returning Unique from a
124760c2415Smrg function. The resource $(D T) can be a polymorphic class object or
125760c2415Smrg instance of an interface, in which case Unique behaves polymorphically
126760c2415Smrg too.
127760c2415Smrg
128760c2415Smrg If $(D T) is a value type, then $(D Unique!T) will be implemented
129760c2415Smrg as a reference to a $(D T).
130760c2415Smrg */
Unique(T)131760c2415Smrg struct Unique(T)
132760c2415Smrg {
133760c2415Smrg /** Represents a reference to $(D T). Resolves to $(D T*) if $(D T) is a value type. */
134760c2415Smrg static if (is(T == class) || is(T == interface))
135760c2415Smrg alias RefT = T;
136760c2415Smrg else
137760c2415Smrg alias RefT = T*;
138760c2415Smrg
139760c2415Smrg public:
140760c2415Smrg // Deferred in case we get some language support for checking uniqueness.
141760c2415Smrg version (None)
142760c2415Smrg /**
143760c2415Smrg Allows safe construction of $(D Unique). It creates the resource and
144760c2415Smrg guarantees unique ownership of it (unless $(D T) publishes aliases of
145760c2415Smrg $(D this)).
146760c2415Smrg Note: Nested structs/classes cannot be created.
147760c2415Smrg Params:
148760c2415Smrg args = Arguments to pass to $(D T)'s constructor.
149760c2415Smrg ---
150760c2415Smrg static class C {}
151760c2415Smrg auto u = Unique!(C).create();
152760c2415Smrg ---
153760c2415Smrg */
154760c2415Smrg static Unique!T create(A...)(auto ref A args)
155760c2415Smrg if (__traits(compiles, new T(args)))
156760c2415Smrg {
157760c2415Smrg debug(Unique) writeln("Unique.create for ", T.stringof);
158760c2415Smrg Unique!T u;
159760c2415Smrg u._p = new T(args);
160760c2415Smrg return u;
161760c2415Smrg }
162760c2415Smrg
163760c2415Smrg /**
164760c2415Smrg Constructor that takes an rvalue.
165760c2415Smrg It will ensure uniqueness, as long as the rvalue
166760c2415Smrg isn't just a view on an lvalue (e.g., a cast).
167760c2415Smrg Typical usage:
168760c2415Smrg ----
169760c2415Smrg Unique!Foo f = new Foo;
170760c2415Smrg ----
171760c2415Smrg */
172760c2415Smrg this(RefT p)
173760c2415Smrg {
174760c2415Smrg debug(Unique) writeln("Unique constructor with rvalue");
175760c2415Smrg _p = p;
176760c2415Smrg }
177760c2415Smrg /**
178760c2415Smrg Constructor that takes an lvalue. It nulls its source.
179760c2415Smrg The nulling will ensure uniqueness as long as there
180760c2415Smrg are no previous aliases to the source.
181760c2415Smrg */
182760c2415Smrg this(ref RefT p)
183760c2415Smrg {
184760c2415Smrg _p = p;
185760c2415Smrg debug(Unique) writeln("Unique constructor nulling source");
186760c2415Smrg p = null;
187760c2415Smrg assert(p is null);
188760c2415Smrg }
189760c2415Smrg /**
190760c2415Smrg Constructor that takes a $(D Unique) of a type that is convertible to our type.
191760c2415Smrg
192760c2415Smrg Typically used to transfer a $(D Unique) rvalue of derived type to
193760c2415Smrg a $(D Unique) of base type.
194760c2415Smrg Example:
195760c2415Smrg ---
196760c2415Smrg class C : Object {}
197760c2415Smrg
198760c2415Smrg Unique!C uc = new C;
199760c2415Smrg Unique!Object uo = uc.release;
200760c2415Smrg ---
201760c2415Smrg */
202760c2415Smrg this(U)(Unique!U u)
203760c2415Smrg if (is(u.RefT:RefT))
204760c2415Smrg {
205760c2415Smrg debug(Unique) writeln("Unique constructor converting from ", U.stringof);
206760c2415Smrg _p = u._p;
207760c2415Smrg u._p = null;
208760c2415Smrg }
209760c2415Smrg
210760c2415Smrg /// Transfer ownership from a $(D Unique) of a type that is convertible to our type.
211760c2415Smrg void opAssign(U)(Unique!U u)
212760c2415Smrg if (is(u.RefT:RefT))
213760c2415Smrg {
214760c2415Smrg debug(Unique) writeln("Unique opAssign converting from ", U.stringof);
215760c2415Smrg // first delete any resource we own
216760c2415Smrg destroy(this);
217760c2415Smrg _p = u._p;
218760c2415Smrg u._p = null;
219760c2415Smrg }
220760c2415Smrg
221760c2415Smrg ~this()
222760c2415Smrg {
223760c2415Smrg debug(Unique) writeln("Unique destructor of ", (_p is null)? null: _p);
224760c2415Smrg if (_p !is null)
225760c2415Smrg {
226760c2415Smrg destroy(_p);
227760c2415Smrg _p = null;
228760c2415Smrg }
229760c2415Smrg }
230760c2415Smrg
231760c2415Smrg /** Returns whether the resource exists. */
232760c2415Smrg @property bool isEmpty() const
233760c2415Smrg {
234760c2415Smrg return _p is null;
235760c2415Smrg }
236760c2415Smrg /** Transfer ownership to a $(D Unique) rvalue. Nullifies the current contents.
237760c2415Smrg Same as calling std.algorithm.move on it.
238760c2415Smrg */
239760c2415Smrg Unique release()
240760c2415Smrg {
241760c2415Smrg debug(Unique) writeln("Unique Release");
242760c2415Smrg import std.algorithm.mutation : move;
243760c2415Smrg return this.move;
244760c2415Smrg }
245760c2415Smrg
246760c2415Smrg /** Forwards member access to contents. */
247760c2415Smrg mixin Proxy!_p;
248760c2415Smrg
249760c2415Smrg /**
250760c2415Smrg Postblit operator is undefined to prevent the cloning of $(D Unique) objects.
251760c2415Smrg */
252760c2415Smrg @disable this(this);
253760c2415Smrg
254760c2415Smrg private:
255760c2415Smrg RefT _p;
256760c2415Smrg }
257760c2415Smrg
258760c2415Smrg ///
259760c2415Smrg @system unittest
260760c2415Smrg {
261760c2415Smrg static struct S
262760c2415Smrg {
263760c2415Smrg int i;
thisS264760c2415Smrg this(int i){this.i = i;}
265760c2415Smrg }
266760c2415Smrg Unique!S produce()
267760c2415Smrg {
268760c2415Smrg // Construct a unique instance of S on the heap
269760c2415Smrg Unique!S ut = new S(5);
270760c2415Smrg // Implicit transfer of ownership
271760c2415Smrg return ut;
272760c2415Smrg }
273760c2415Smrg // Borrow a unique resource by ref
274760c2415Smrg void increment(ref Unique!S ur)
275760c2415Smrg {
276760c2415Smrg ur.i++;
277760c2415Smrg }
278760c2415Smrg void consume(Unique!S u2)
279760c2415Smrg {
280760c2415Smrg assert(u2.i == 6);
281760c2415Smrg // Resource automatically deleted here
282760c2415Smrg }
283760c2415Smrg Unique!S u1;
284760c2415Smrg assert(u1.isEmpty);
285760c2415Smrg u1 = produce();
286760c2415Smrg increment(u1);
287760c2415Smrg assert(u1.i == 6);
288760c2415Smrg //consume(u1); // Error: u1 is not copyable
289760c2415Smrg // Transfer ownership of the resource
290760c2415Smrg consume(u1.release);
291760c2415Smrg assert(u1.isEmpty);
292760c2415Smrg }
293760c2415Smrg
294760c2415Smrg @system unittest
295760c2415Smrg {
296760c2415Smrg // test conversion to base ref
297760c2415Smrg int deleted = 0;
298760c2415Smrg class C
299760c2415Smrg {
~this()300760c2415Smrg ~this(){deleted++;}
301760c2415Smrg }
302760c2415Smrg // constructor conversion
303760c2415Smrg Unique!Object u = Unique!C(new C);
304760c2415Smrg static assert(!__traits(compiles, {u = new C;}));
305760c2415Smrg assert(!u.isEmpty);
306760c2415Smrg destroy(u);
307760c2415Smrg assert(deleted == 1);
308760c2415Smrg
309760c2415Smrg Unique!C uc = new C;
310760c2415Smrg static assert(!__traits(compiles, {Unique!Object uo = uc;}));
311760c2415Smrg Unique!Object uo = new C;
312760c2415Smrg // opAssign conversion, deleting uo resource first
313760c2415Smrg uo = uc.release;
314760c2415Smrg assert(uc.isEmpty);
315760c2415Smrg assert(!uo.isEmpty);
316760c2415Smrg assert(deleted == 2);
317760c2415Smrg }
318760c2415Smrg
319760c2415Smrg @system unittest
320760c2415Smrg {
321760c2415Smrg debug(Unique) writeln("Unique class");
322760c2415Smrg class Bar
323760c2415Smrg {
~this()324760c2415Smrg ~this() { debug(Unique) writeln(" Bar destructor"); }
val()325760c2415Smrg int val() const { return 4; }
326760c2415Smrg }
327760c2415Smrg alias UBar = Unique!(Bar);
g(UBar u)328760c2415Smrg UBar g(UBar u)
329760c2415Smrg {
330760c2415Smrg debug(Unique) writeln("inside g");
331760c2415Smrg return u.release;
332760c2415Smrg }
333760c2415Smrg auto ub = UBar(new Bar);
334760c2415Smrg assert(!ub.isEmpty);
335760c2415Smrg assert(ub.val == 4);
336760c2415Smrg static assert(!__traits(compiles, {auto ub3 = g(ub);}));
337760c2415Smrg debug(Unique) writeln("Calling g");
338760c2415Smrg auto ub2 = g(ub.release);
339760c2415Smrg debug(Unique) writeln("Returned from g");
340760c2415Smrg assert(ub.isEmpty);
341760c2415Smrg assert(!ub2.isEmpty);
342760c2415Smrg }
343760c2415Smrg
344760c2415Smrg @system unittest
345760c2415Smrg {
346760c2415Smrg debug(Unique) writeln("Unique interface");
347760c2415Smrg interface Bar
348760c2415Smrg {
349760c2415Smrg int val() const;
350760c2415Smrg }
351760c2415Smrg class BarImpl : Bar
352760c2415Smrg {
353760c2415Smrg static int count;
this()354760c2415Smrg this()
355760c2415Smrg {
356760c2415Smrg count++;
357760c2415Smrg }
~this()358760c2415Smrg ~this()
359760c2415Smrg {
360760c2415Smrg count--;
361760c2415Smrg }
val()362760c2415Smrg int val() const { return 4; }
363760c2415Smrg }
364760c2415Smrg alias UBar = Unique!Bar;
g(UBar u)365760c2415Smrg UBar g(UBar u)
366760c2415Smrg {
367760c2415Smrg debug(Unique) writeln("inside g");
368760c2415Smrg return u.release;
369760c2415Smrg }
consume(UBar u)370760c2415Smrg void consume(UBar u)
371760c2415Smrg {
372760c2415Smrg assert(u.val() == 4);
373760c2415Smrg // Resource automatically deleted here
374760c2415Smrg }
375760c2415Smrg auto ub = UBar(new BarImpl);
376760c2415Smrg assert(BarImpl.count == 1);
377760c2415Smrg assert(!ub.isEmpty);
378760c2415Smrg assert(ub.val == 4);
379760c2415Smrg static assert(!__traits(compiles, {auto ub3 = g(ub);}));
380760c2415Smrg debug(Unique) writeln("Calling g");
381760c2415Smrg auto ub2 = g(ub.release);
382760c2415Smrg debug(Unique) writeln("Returned from g");
383760c2415Smrg assert(ub.isEmpty);
384760c2415Smrg assert(!ub2.isEmpty);
385760c2415Smrg consume(ub2.release);
386760c2415Smrg assert(BarImpl.count == 0);
387760c2415Smrg }
388760c2415Smrg
389760c2415Smrg @system unittest
390760c2415Smrg {
391760c2415Smrg debug(Unique) writeln("Unique struct");
392760c2415Smrg struct Foo
393760c2415Smrg {
~thisFoo394760c2415Smrg ~this() { debug(Unique) writeln(" Foo destructor"); }
valFoo395760c2415Smrg int val() const { return 3; }
396760c2415Smrg @disable this(this);
397760c2415Smrg }
398760c2415Smrg alias UFoo = Unique!(Foo);
399760c2415Smrg
f(UFoo u)400760c2415Smrg UFoo f(UFoo u)
401760c2415Smrg {
402760c2415Smrg debug(Unique) writeln("inside f");
403760c2415Smrg return u.release;
404760c2415Smrg }
405760c2415Smrg
406760c2415Smrg auto uf = UFoo(new Foo);
407760c2415Smrg assert(!uf.isEmpty);
408760c2415Smrg assert(uf.val == 3);
409760c2415Smrg static assert(!__traits(compiles, {auto uf3 = f(uf);}));
410760c2415Smrg debug(Unique) writeln("Unique struct: calling f");
411760c2415Smrg auto uf2 = f(uf.release);
412760c2415Smrg debug(Unique) writeln("Unique struct: returned from f");
413760c2415Smrg assert(uf.isEmpty);
414760c2415Smrg assert(!uf2.isEmpty);
415760c2415Smrg }
416760c2415Smrg
417760c2415Smrg // ensure Unique behaves correctly through const access paths
418760c2415Smrg @system unittest
419760c2415Smrg {
420760c2415Smrg struct Bar {int val;}
421760c2415Smrg struct Foo
422760c2415Smrg {
423760c2415Smrg Unique!Bar bar = new Bar;
424760c2415Smrg }
425760c2415Smrg
426760c2415Smrg Foo foo;
427760c2415Smrg foo.bar.val = 6;
428760c2415Smrg const Foo* ptr = &foo;
429760c2415Smrg static assert(is(typeof(ptr) == const(Foo*)));
430760c2415Smrg static assert(is(typeof(ptr.bar) == const(Unique!Bar)));
431760c2415Smrg static assert(is(typeof(ptr.bar.val) == const(int)));
432760c2415Smrg assert(ptr.bar.val == 6);
433760c2415Smrg foo.bar.val = 7;
434760c2415Smrg assert(ptr.bar.val == 7);
435760c2415Smrg }
436760c2415Smrg
437760c2415Smrg // Used in Tuple.toString
438760c2415Smrg private template sharedToString(alias field)
439760c2415Smrg if (is(typeof(field) == shared))
440760c2415Smrg {
441760c2415Smrg static immutable sharedToString = typeof(field).stringof;
442760c2415Smrg }
443760c2415Smrg
444760c2415Smrg private template sharedToString(alias field)
445760c2415Smrg if (!is(typeof(field) == shared))
446760c2415Smrg {
447760c2415Smrg alias sharedToString = field;
448760c2415Smrg }
449760c2415Smrg
450760c2415Smrg /**
451760c2415Smrg _Tuple of values, for example $(D Tuple!(int, string)) is a record that
452760c2415Smrg stores an $(D int) and a $(D string). $(D Tuple) can be used to bundle
453760c2415Smrg values together, notably when returning multiple values from a
454760c2415Smrg function. If $(D obj) is a `Tuple`, the individual members are
455760c2415Smrg accessible with the syntax $(D obj[0]) for the first field, $(D obj[1])
456760c2415Smrg for the second, and so on.
457760c2415Smrg
458760c2415Smrg The choice of zero-based indexing instead of one-base indexing was
459760c2415Smrg motivated by the ability to use value tuples with various compile-time
460760c2415Smrg loop constructs (e.g. $(REF AliasSeq, std,meta) iteration), all of which use
461760c2415Smrg zero-based indexing.
462760c2415Smrg
463760c2415Smrg See_Also: $(LREF tuple).
464760c2415Smrg
465760c2415Smrg Params:
466760c2415Smrg Specs = A list of types (and optionally, member names) that the `Tuple` contains.
467760c2415Smrg */
Tuple(Specs...)468760c2415Smrg template Tuple(Specs...)
469760c2415Smrg {
470760c2415Smrg import std.meta : staticMap;
471760c2415Smrg
472760c2415Smrg // Parse (type,name) pairs (FieldSpecs) out of the specified
473760c2415Smrg // arguments. Some fields would have name, others not.
474760c2415Smrg template parseSpecs(Specs...)
475760c2415Smrg {
476760c2415Smrg static if (Specs.length == 0)
477760c2415Smrg {
478760c2415Smrg alias parseSpecs = AliasSeq!();
479760c2415Smrg }
480760c2415Smrg else static if (is(Specs[0]))
481760c2415Smrg {
482760c2415Smrg static if (is(typeof(Specs[1]) : string))
483760c2415Smrg {
484760c2415Smrg alias parseSpecs =
485760c2415Smrg AliasSeq!(FieldSpec!(Specs[0 .. 2]),
486760c2415Smrg parseSpecs!(Specs[2 .. $]));
487760c2415Smrg }
488760c2415Smrg else
489760c2415Smrg {
490760c2415Smrg alias parseSpecs =
491760c2415Smrg AliasSeq!(FieldSpec!(Specs[0]),
492760c2415Smrg parseSpecs!(Specs[1 .. $]));
493760c2415Smrg }
494760c2415Smrg }
495760c2415Smrg else
496760c2415Smrg {
497760c2415Smrg static assert(0, "Attempted to instantiate Tuple with an "
498760c2415Smrg ~"invalid argument: "~ Specs[0].stringof);
499760c2415Smrg }
500760c2415Smrg }
501760c2415Smrg
502760c2415Smrg template FieldSpec(T, string s = "")
503760c2415Smrg {
504760c2415Smrg alias Type = T;
505760c2415Smrg alias name = s;
506760c2415Smrg }
507760c2415Smrg
508760c2415Smrg alias fieldSpecs = parseSpecs!Specs;
509760c2415Smrg
510760c2415Smrg // Used with staticMap.
511760c2415Smrg alias extractType(alias spec) = spec.Type;
512760c2415Smrg alias extractName(alias spec) = spec.name;
513760c2415Smrg
514760c2415Smrg // Generates named fields as follows:
515760c2415Smrg // alias name_0 = Identity!(field[0]);
516760c2415Smrg // alias name_1 = Identity!(field[1]);
517760c2415Smrg // :
518760c2415Smrg // NOTE: field[k] is an expression (which yields a symbol of a
519760c2415Smrg // variable) and can't be aliased directly.
520760c2415Smrg string injectNamedFields()
521760c2415Smrg {
522760c2415Smrg string decl = "";
523760c2415Smrg foreach (i, name; staticMap!(extractName, fieldSpecs))
524760c2415Smrg {
525760c2415Smrg import std.format : format;
526760c2415Smrg
527760c2415Smrg decl ~= format("alias _%s = Identity!(field[%s]);", i, i);
528760c2415Smrg if (name.length != 0)
529760c2415Smrg {
530760c2415Smrg decl ~= format("alias %s = _%s;", name, i);
531760c2415Smrg }
532760c2415Smrg }
533760c2415Smrg return decl;
534760c2415Smrg }
535760c2415Smrg
536760c2415Smrg // Returns Specs for a subtuple this[from .. to] preserving field
537760c2415Smrg // names if any.
538760c2415Smrg alias sliceSpecs(size_t from, size_t to) =
539760c2415Smrg staticMap!(expandSpec, fieldSpecs[from .. to]);
540760c2415Smrg
541760c2415Smrg template expandSpec(alias spec)
542760c2415Smrg {
543760c2415Smrg static if (spec.name.length == 0)
544760c2415Smrg {
545760c2415Smrg alias expandSpec = AliasSeq!(spec.Type);
546760c2415Smrg }
547760c2415Smrg else
548760c2415Smrg {
549760c2415Smrg alias expandSpec = AliasSeq!(spec.Type, spec.name);
550760c2415Smrg }
551760c2415Smrg }
552760c2415Smrg
553760c2415Smrg enum areCompatibleTuples(Tup1, Tup2, string op) = isTuple!Tup2 && is(typeof(
554760c2415Smrg (ref Tup1 tup1, ref Tup2 tup2)
555760c2415Smrg {
556760c2415Smrg static assert(tup1.field.length == tup2.field.length);
557760c2415Smrg foreach (i, _; Tup1.Types)
558760c2415Smrg {
559760c2415Smrg auto lhs = typeof(tup1.field[i]).init;
560760c2415Smrg auto rhs = typeof(tup2.field[i]).init;
561760c2415Smrg static if (op == "=")
562760c2415Smrg lhs = rhs;
563760c2415Smrg else
564760c2415Smrg auto result = mixin("lhs "~op~" rhs");
565760c2415Smrg }
566760c2415Smrg }));
567760c2415Smrg
568760c2415Smrg enum areBuildCompatibleTuples(Tup1, Tup2) = isTuple!Tup2 && is(typeof(
569760c2415Smrg {
570760c2415Smrg static assert(Tup1.Types.length == Tup2.Types.length);
571760c2415Smrg foreach (i, _; Tup1.Types)
572760c2415Smrg static assert(isBuildable!(Tup1.Types[i], Tup2.Types[i]));
573760c2415Smrg }));
574760c2415Smrg
575760c2415Smrg /+ Returns $(D true) iff a $(D T) can be initialized from a $(D U). +/
576760c2415Smrg enum isBuildable(T, U) = is(typeof(
577760c2415Smrg {
578760c2415Smrg U u = U.init;
579760c2415Smrg T t = u;
580760c2415Smrg }));
581760c2415Smrg /+ Helper for partial instanciation +/
582760c2415Smrg template isBuildableFrom(U)
583760c2415Smrg {
584760c2415Smrg enum isBuildableFrom(T) = isBuildable!(T, U);
585760c2415Smrg }
586760c2415Smrg
587760c2415Smrg struct Tuple
588760c2415Smrg {
589760c2415Smrg /**
590760c2415Smrg * The types of the `Tuple`'s components.
591760c2415Smrg */
592760c2415Smrg alias Types = staticMap!(extractType, fieldSpecs);
593760c2415Smrg
594760c2415Smrg ///
595760c2415Smrg static if (Specs.length == 0) @safe unittest
596760c2415Smrg {
597760c2415Smrg alias Fields = Tuple!(int, "id", string, float);
598760c2415Smrg static assert(is(Fields.Types == AliasSeq!(int, string, float)));
599760c2415Smrg }
600760c2415Smrg
601760c2415Smrg /**
602760c2415Smrg * The names of the `Tuple`'s components. Unnamed fields have empty names.
603760c2415Smrg */
604760c2415Smrg alias fieldNames = staticMap!(extractName, fieldSpecs);
605760c2415Smrg
606760c2415Smrg ///
607760c2415Smrg static if (Specs.length == 0) @safe unittest
608760c2415Smrg {
609760c2415Smrg alias Fields = Tuple!(int, "id", string, float);
610760c2415Smrg static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
611760c2415Smrg }
612760c2415Smrg
613760c2415Smrg /**
614760c2415Smrg * Use $(D t.expand) for a `Tuple` $(D t) to expand it into its
615760c2415Smrg * components. The result of $(D expand) acts as if the `Tuple`'s components
616760c2415Smrg * were listed as a list of values. (Ordinarily, a $(D Tuple) acts as a
617760c2415Smrg * single value.)
618760c2415Smrg */
619760c2415Smrg Types expand;
620760c2415Smrg mixin(injectNamedFields());
621760c2415Smrg
622760c2415Smrg ///
623760c2415Smrg static if (Specs.length == 0) @safe unittest
624760c2415Smrg {
625760c2415Smrg auto t1 = tuple(1, " hello ", 2.3);
626760c2415Smrg assert(t1.toString() == `Tuple!(int, string, double)(1, " hello ", 2.3)`);
627760c2415Smrg
628760c2415Smrg void takeSeveralTypes(int n, string s, bool b)
629760c2415Smrg {
630760c2415Smrg assert(n == 4 && s == "test" && b == false);
631760c2415Smrg }
632760c2415Smrg
633760c2415Smrg auto t2 = tuple(4, "test", false);
634760c2415Smrg //t.expand acting as a list of values
635760c2415Smrg takeSeveralTypes(t2.expand);
636760c2415Smrg }
637760c2415Smrg
638760c2415Smrg static if (is(Specs))
639760c2415Smrg {
640760c2415Smrg // This is mostly to make t[n] work.
641760c2415Smrg alias expand this;
642760c2415Smrg }
643760c2415Smrg else
644760c2415Smrg {
645760c2415Smrg @property
646760c2415Smrg ref inout(Tuple!Types) _Tuple_super() inout @trusted
647760c2415Smrg {
648760c2415Smrg foreach (i, _; Types) // Rely on the field layout
649760c2415Smrg {
650760c2415Smrg static assert(typeof(return).init.tupleof[i].offsetof ==
651760c2415Smrg expand[i].offsetof);
652760c2415Smrg }
653760c2415Smrg return *cast(typeof(return)*) &(field[0]);
654760c2415Smrg }
655760c2415Smrg // This is mostly to make t[n] work.
656760c2415Smrg alias _Tuple_super this;
657760c2415Smrg }
658760c2415Smrg
659760c2415Smrg // backwards compatibility
660760c2415Smrg alias field = expand;
661760c2415Smrg
662760c2415Smrg /**
663760c2415Smrg * Constructor taking one value for each field.
664760c2415Smrg *
665760c2415Smrg * Params:
666760c2415Smrg * values = A list of values that are either the same
667760c2415Smrg * types as those given by the `Types` field
668760c2415Smrg * of this `Tuple`, or can implicitly convert
669760c2415Smrg * to those types. They must be in the same
670760c2415Smrg * order as they appear in `Types`.
671760c2415Smrg */
672760c2415Smrg static if (Types.length > 0)
673760c2415Smrg {
674760c2415Smrg this(Types values)
675760c2415Smrg {
676760c2415Smrg field[] = values[];
677760c2415Smrg }
678760c2415Smrg }
679760c2415Smrg
680760c2415Smrg ///
681760c2415Smrg static if (Specs.length == 0) @safe unittest
682760c2415Smrg {
683760c2415Smrg alias ISD = Tuple!(int, string, double);
684760c2415Smrg auto tup = ISD(1, "test", 3.2);
685760c2415Smrg assert(tup.toString() == `Tuple!(int, string, double)(1, "test", 3.2)`);
686760c2415Smrg }
687760c2415Smrg
688760c2415Smrg /**
689760c2415Smrg * Constructor taking a compatible array.
690760c2415Smrg *
691760c2415Smrg * Params:
692760c2415Smrg * values = A compatible static array to build the `Tuple` from.
693760c2415Smrg * Array slices are not supported.
694760c2415Smrg */
695760c2415Smrg this(U, size_t n)(U[n] values)
696760c2415Smrg if (n == Types.length && allSatisfy!(isBuildableFrom!U, Types))
697760c2415Smrg {
698760c2415Smrg foreach (i, _; Types)
699760c2415Smrg {
700760c2415Smrg field[i] = values[i];
701760c2415Smrg }
702760c2415Smrg }
703760c2415Smrg
704760c2415Smrg ///
705760c2415Smrg static if (Specs.length == 0) @safe unittest
706760c2415Smrg {
707760c2415Smrg int[2] ints;
708760c2415Smrg Tuple!(int, int) t = ints;
709760c2415Smrg }
710760c2415Smrg
711760c2415Smrg /**
712760c2415Smrg * Constructor taking a compatible `Tuple`. Two `Tuple`s are compatible
713760c2415Smrg * $(B iff) they are both of the same length, and, for each type `T` on the
714760c2415Smrg * left-hand side, the corresponding type `U` on the right-hand side can
715760c2415Smrg * implicitly convert to `T`.
716760c2415Smrg *
717760c2415Smrg * Params:
718760c2415Smrg * another = A compatible `Tuple` to build from. Its type must be
719760c2415Smrg * compatible with the target `Tuple`'s type.
720760c2415Smrg */
721760c2415Smrg this(U)(U another)
722760c2415Smrg if (areBuildCompatibleTuples!(typeof(this), U))
723760c2415Smrg {
724760c2415Smrg field[] = another.field[];
725760c2415Smrg }
726760c2415Smrg
727760c2415Smrg ///
728760c2415Smrg static if (Specs.length == 0) @safe unittest
729760c2415Smrg {
730760c2415Smrg alias IntVec = Tuple!(int, int, int);
731760c2415Smrg alias DubVec = Tuple!(double, double, double);
732760c2415Smrg
733760c2415Smrg IntVec iv = tuple(1, 1, 1);
734760c2415Smrg
735760c2415Smrg //Ok, int can implicitly convert to double
736760c2415Smrg DubVec dv = iv;
737760c2415Smrg //Error: double cannot implicitly convert to int
738760c2415Smrg //IntVec iv2 = dv;
739760c2415Smrg }
740760c2415Smrg
741760c2415Smrg /**
742760c2415Smrg * Comparison for equality. Two `Tuple`s are considered equal
743760c2415Smrg * $(B iff) they fulfill the following criteria:
744760c2415Smrg *
745760c2415Smrg * $(UL
746760c2415Smrg * $(LI Each `Tuple` is the same length.)
747760c2415Smrg * $(LI For each type `T` on the left-hand side and each type
748760c2415Smrg * `U` on the right-hand side, values of type `T` can be
749760c2415Smrg * compared with values of type `U`.)
750760c2415Smrg * $(LI For each value `v1` on the left-hand side and each value
751760c2415Smrg * `v2` on the right-hand side, the expression `v1 == v2` is
752760c2415Smrg * true.))
753760c2415Smrg *
754760c2415Smrg * Params:
755760c2415Smrg * rhs = The `Tuple` to compare against. It must meeting the criteria
756760c2415Smrg * for comparison between `Tuple`s.
757760c2415Smrg *
758760c2415Smrg * Returns:
759760c2415Smrg * true if both `Tuple`s are equal, otherwise false.
760760c2415Smrg */
761760c2415Smrg bool opEquals(R)(R rhs)
762760c2415Smrg if (areCompatibleTuples!(typeof(this), R, "=="))
763760c2415Smrg {
764760c2415Smrg return field[] == rhs.field[];
765760c2415Smrg }
766760c2415Smrg
767760c2415Smrg /// ditto
768760c2415Smrg bool opEquals(R)(R rhs) const
769760c2415Smrg if (areCompatibleTuples!(typeof(this), R, "=="))
770760c2415Smrg {
771760c2415Smrg return field[] == rhs.field[];
772760c2415Smrg }
773760c2415Smrg
774760c2415Smrg ///
775760c2415Smrg static if (Specs.length == 0) @safe unittest
776760c2415Smrg {
777760c2415Smrg Tuple!(int, string) t1 = tuple(1, "test");
778760c2415Smrg Tuple!(double, string) t2 = tuple(1.0, "test");
779760c2415Smrg //Ok, int can be compared with double and
780760c2415Smrg //both have a value of 1
781760c2415Smrg assert(t1 == t2);
782760c2415Smrg }
783760c2415Smrg
784760c2415Smrg /**
785760c2415Smrg * Comparison for ordering.
786760c2415Smrg *
787760c2415Smrg * Params:
788760c2415Smrg * rhs = The `Tuple` to compare against. It must meet the criteria
789760c2415Smrg * for comparison between `Tuple`s.
790760c2415Smrg *
791760c2415Smrg * Returns:
792760c2415Smrg * For any values `v1` on the right-hand side and `v2` on the
793760c2415Smrg * left-hand side:
794760c2415Smrg *
795760c2415Smrg * $(UL
796760c2415Smrg * $(LI A negative integer if the expression `v1 < v2` is true.)
797760c2415Smrg * $(LI A positive integer if the expression `v1 > v2` is true.)
798760c2415Smrg * $(LI 0 if the expression `v1 == v2` is true.))
799760c2415Smrg */
800760c2415Smrg int opCmp(R)(R rhs)
801760c2415Smrg if (areCompatibleTuples!(typeof(this), R, "<"))
802760c2415Smrg {
803760c2415Smrg foreach (i, Unused; Types)
804760c2415Smrg {
805760c2415Smrg if (field[i] != rhs.field[i])
806760c2415Smrg {
807760c2415Smrg return field[i] < rhs.field[i] ? -1 : 1;
808760c2415Smrg }
809760c2415Smrg }
810760c2415Smrg return 0;
811760c2415Smrg }
812760c2415Smrg
813760c2415Smrg /// ditto
814760c2415Smrg int opCmp(R)(R rhs) const
815760c2415Smrg if (areCompatibleTuples!(typeof(this), R, "<"))
816760c2415Smrg {
817760c2415Smrg foreach (i, Unused; Types)
818760c2415Smrg {
819760c2415Smrg if (field[i] != rhs.field[i])
820760c2415Smrg {
821760c2415Smrg return field[i] < rhs.field[i] ? -1 : 1;
822760c2415Smrg }
823760c2415Smrg }
824760c2415Smrg return 0;
825760c2415Smrg }
826760c2415Smrg
827760c2415Smrg /**
828760c2415Smrg The first `v1` for which `v1 > v2` is true determines
829760c2415Smrg the result. This could lead to unexpected behaviour.
830760c2415Smrg */
831760c2415Smrg static if (Specs.length == 0) @safe unittest
832760c2415Smrg {
833760c2415Smrg auto tup1 = tuple(1, 1, 1);
834760c2415Smrg auto tup2 = tuple(1, 100, 100);
835760c2415Smrg assert(tup1 < tup2);
836760c2415Smrg
837760c2415Smrg //Only the first result matters for comparison
838760c2415Smrg tup1[0] = 2;
839760c2415Smrg assert(tup1 > tup2);
840760c2415Smrg }
841760c2415Smrg
842760c2415Smrg /**
843760c2415Smrg * Assignment from another `Tuple`.
844760c2415Smrg *
845760c2415Smrg * Params:
846760c2415Smrg * rhs = The source `Tuple` to assign from. Each element of the
847760c2415Smrg * source `Tuple` must be implicitly assignable to each
848760c2415Smrg * respective element of the target `Tuple`.
849760c2415Smrg */
850760c2415Smrg void opAssign(R)(auto ref R rhs)
851760c2415Smrg if (areCompatibleTuples!(typeof(this), R, "="))
852760c2415Smrg {
853760c2415Smrg import std.algorithm.mutation : swap;
854760c2415Smrg
855760c2415Smrg static if (is(R : Tuple!Types) && !__traits(isRef, rhs))
856760c2415Smrg {
857760c2415Smrg if (__ctfe)
858760c2415Smrg {
859760c2415Smrg // Cannot use swap at compile time
860760c2415Smrg field[] = rhs.field[];
861760c2415Smrg }
862760c2415Smrg else
863760c2415Smrg {
864760c2415Smrg // Use swap-and-destroy to optimize rvalue assignment
865760c2415Smrg swap!(Tuple!Types)(this, rhs);
866760c2415Smrg }
867760c2415Smrg }
868760c2415Smrg else
869760c2415Smrg {
870760c2415Smrg // Do not swap; opAssign should be called on the fields.
871760c2415Smrg field[] = rhs.field[];
872760c2415Smrg }
873760c2415Smrg }
874760c2415Smrg
875760c2415Smrg /**
876760c2415Smrg * Renames the elements of a $(LREF Tuple).
877760c2415Smrg *
878760c2415Smrg * `rename` uses the passed `names` and returns a new
879760c2415Smrg * $(LREF Tuple) using these names, with the content
880760c2415Smrg * unchanged.
881760c2415Smrg * If fewer names are passed than there are members
882760c2415Smrg * of the $(LREF Tuple) then those trailing members are unchanged.
883760c2415Smrg * An empty string will remove the name for that member.
884760c2415Smrg * It is an compile-time error to pass more names than
885760c2415Smrg * there are members of the $(LREF Tuple).
886760c2415Smrg */
887760c2415Smrg ref rename(names...)() return
888760c2415Smrg if (names.length == 0 || allSatisfy!(isSomeString, typeof(names)))
889760c2415Smrg {
890760c2415Smrg import std.algorithm.comparison : equal;
891760c2415Smrg // to circumvent bug 16418
892760c2415Smrg static if (names.length == 0 || equal([names], [fieldNames]))
893760c2415Smrg return this;
894760c2415Smrg else
895760c2415Smrg {
896760c2415Smrg enum nT = Types.length;
897760c2415Smrg enum nN = names.length;
898760c2415Smrg static assert(nN <= nT, "Cannot have more names than tuple members");
899760c2415Smrg alias allNames = AliasSeq!(names, fieldNames[nN .. $]);
900760c2415Smrg
901760c2415Smrg template GetItem(size_t idx)
902760c2415Smrg {
903760c2415Smrg import std.array : empty;
904760c2415Smrg static if (idx < nT)
905760c2415Smrg alias GetItem = Alias!(Types[idx]);
906760c2415Smrg else static if (allNames[idx - nT].empty)
907760c2415Smrg alias GetItem = AliasSeq!();
908760c2415Smrg else
909760c2415Smrg alias GetItem = Alias!(allNames[idx - nT]);
910760c2415Smrg }
911760c2415Smrg
912760c2415Smrg import std.range : roundRobin, iota;
913760c2415Smrg alias NewTupleT = Tuple!(staticMap!(GetItem, aliasSeqOf!(
914760c2415Smrg roundRobin(iota(nT), iota(nT, 2*nT)))));
915760c2415Smrg return *(() @trusted => cast(NewTupleT*)&this)();
916760c2415Smrg }
917760c2415Smrg }
918760c2415Smrg
919760c2415Smrg ///
920760c2415Smrg static if (Specs.length == 0) @safe unittest
921760c2415Smrg {
922760c2415Smrg auto t0 = tuple(4, "hello");
923760c2415Smrg
924760c2415Smrg auto t0Named = t0.rename!("val", "tag");
925760c2415Smrg assert(t0Named.val == 4);
926760c2415Smrg assert(t0Named.tag == "hello");
927760c2415Smrg
928760c2415Smrg Tuple!(float, "dat", size_t[2], "pos") t1;
929760c2415Smrg t1.pos = [2, 1];
930760c2415Smrg auto t1Named = t1.rename!"height";
931760c2415Smrg t1Named.height = 3.4f;
932760c2415Smrg assert(t1Named.height == 3.4f);
933760c2415Smrg assert(t1Named.pos == [2, 1]);
934760c2415Smrg t1Named.rename!"altitude".altitude = 5;
935760c2415Smrg assert(t1Named.height == 5);
936760c2415Smrg
937760c2415Smrg Tuple!(int, "a", int, int, "c") t2;
938760c2415Smrg t2 = tuple(3,4,5);
939760c2415Smrg auto t2Named = t2.rename!("", "b");
940760c2415Smrg // "a" no longer has a name
941760c2415Smrg static assert(!hasMember!(typeof(t2Named), "a"));
942760c2415Smrg assert(t2Named[0] == 3);
943760c2415Smrg assert(t2Named.b == 4);
944760c2415Smrg assert(t2Named.c == 5);
945760c2415Smrg
946760c2415Smrg // not allowed to specify more names than the tuple has members
947760c2415Smrg static assert(!__traits(compiles, t2.rename!("a","b","c","d")));
948760c2415Smrg
949760c2415Smrg // use it in a range pipeline
950760c2415Smrg import std.range : iota, zip;
951760c2415Smrg import std.algorithm.iteration : map, sum;
952760c2415Smrg auto res = zip(iota(1, 4), iota(10, 13))
953760c2415Smrg .map!(t => t.rename!("a", "b"))
954760c2415Smrg .map!(t => t.a * t.b)
955760c2415Smrg .sum;
956760c2415Smrg assert(res == 68);
957760c2415Smrg }
958760c2415Smrg
959760c2415Smrg /**
960760c2415Smrg * Overload of $(LREF _rename) that takes an associative array
961760c2415Smrg * `translate` as a template parameter, where the keys are
962760c2415Smrg * either the names or indices of the members to be changed
963760c2415Smrg * and the new names are the corresponding values.
964760c2415Smrg * Every key in `translate` must be the name of a member of the
965760c2415Smrg * $(LREF tuple).
966760c2415Smrg * The same rules for empty strings apply as for the variadic
967760c2415Smrg * template overload of $(LREF _rename).
968760c2415Smrg */
969760c2415Smrg ref rename(alias translate)()
970760c2415Smrg if (is(typeof(translate) : V[K], V, K) && isSomeString!V &&
971760c2415Smrg (isSomeString!K || is(K : size_t)))
972760c2415Smrg {
973760c2415Smrg import std.range : ElementType;
974760c2415Smrg static if (isSomeString!(ElementType!(typeof(translate.keys))))
975760c2415Smrg {
976760c2415Smrg {
977760c2415Smrg import std.conv : to;
978760c2415Smrg import std.algorithm.iteration : filter;
979760c2415Smrg import std.algorithm.searching : canFind;
980760c2415Smrg enum notFound = translate.keys
981760c2415Smrg .filter!(k => fieldNames.canFind(k) == -1);
982760c2415Smrg static assert(notFound.empty, "Cannot find members "
983760c2415Smrg ~ notFound.to!string ~ " in type "
984760c2415Smrg ~ typeof(this).stringof);
985760c2415Smrg }
986760c2415Smrg return this.rename!(aliasSeqOf!(
987760c2415Smrg {
988760c2415Smrg import std.array : empty;
989760c2415Smrg auto names = [fieldNames];
990760c2415Smrg foreach (ref n; names)
991760c2415Smrg if (!n.empty)
992760c2415Smrg if (auto p = n in translate)
993760c2415Smrg n = *p;
994760c2415Smrg return names;
995760c2415Smrg }()));
996760c2415Smrg }
997760c2415Smrg else
998760c2415Smrg {
999760c2415Smrg {
1000760c2415Smrg import std.algorithm.iteration : filter;
1001760c2415Smrg import std.conv : to;
1002760c2415Smrg enum invalid = translate.keys.
1003760c2415Smrg filter!(k => k < 0 || k >= this.length);
1004760c2415Smrg static assert(invalid.empty, "Indices " ~ invalid.to!string
1005760c2415Smrg ~ " are out of bounds for tuple with length "
1006760c2415Smrg ~ this.length.to!string);
1007760c2415Smrg }
1008760c2415Smrg return this.rename!(aliasSeqOf!(
1009760c2415Smrg {
1010760c2415Smrg auto names = [fieldNames];
1011760c2415Smrg foreach (k, v; translate)
1012760c2415Smrg names[k] = v;
1013760c2415Smrg return names;
1014760c2415Smrg }()));
1015760c2415Smrg }
1016760c2415Smrg }
1017760c2415Smrg
1018760c2415Smrg ///
1019760c2415Smrg static if (Specs.length == 0) @safe unittest
1020760c2415Smrg {
1021760c2415Smrg //replacing names by their current name
1022760c2415Smrg
1023760c2415Smrg Tuple!(float, "dat", size_t[2], "pos") t1;
1024760c2415Smrg t1.pos = [2, 1];
1025760c2415Smrg auto t1Named = t1.rename!(["dat": "height"]);
1026760c2415Smrg t1Named.height = 3.4;
1027760c2415Smrg assert(t1Named.pos == [2, 1]);
1028760c2415Smrg t1Named.rename!(["height": "altitude"]).altitude = 5;
1029760c2415Smrg assert(t1Named.height == 5);
1030760c2415Smrg
1031760c2415Smrg Tuple!(int, "a", int, "b") t2;
1032760c2415Smrg t2 = tuple(3, 4);
1033760c2415Smrg auto t2Named = t2.rename!(["a": "b", "b": "c"]);
1034760c2415Smrg assert(t2Named.b == 3);
1035760c2415Smrg assert(t2Named.c == 4);
1036760c2415Smrg }
1037760c2415Smrg
1038760c2415Smrg ///
1039760c2415Smrg static if (Specs.length == 0) @safe unittest
1040760c2415Smrg {
1041760c2415Smrg //replace names by their position
1042760c2415Smrg
1043760c2415Smrg Tuple!(float, "dat", size_t[2], "pos") t1;
1044760c2415Smrg t1.pos = [2, 1];
1045760c2415Smrg auto t1Named = t1.rename!([0: "height"]);
1046760c2415Smrg t1Named.height = 3.4;
1047760c2415Smrg assert(t1Named.pos == [2, 1]);
1048760c2415Smrg t1Named.rename!([0: "altitude"]).altitude = 5;
1049760c2415Smrg assert(t1Named.height == 5);
1050760c2415Smrg
1051760c2415Smrg Tuple!(int, "a", int, "b", int, "c") t2;
1052760c2415Smrg t2 = tuple(3, 4, 5);
1053760c2415Smrg auto t2Named = t2.rename!([0: "c", 2: "a"]);
1054760c2415Smrg assert(t2Named.a == 5);
1055760c2415Smrg assert(t2Named.b == 4);
1056760c2415Smrg assert(t2Named.c == 3);
1057760c2415Smrg }
1058760c2415Smrg
1059760c2415Smrg static if (Specs.length == 0) @safe unittest
1060760c2415Smrg {
1061760c2415Smrg //check that empty translations work fine
1062760c2415Smrg enum string[string] a0 = null;
1063760c2415Smrg enum string[int] a1 = null;
1064760c2415Smrg Tuple!(float, "a", float, "b") t0;
1065760c2415Smrg
1066760c2415Smrg auto t1 = t0.rename!a0;
1067760c2415Smrg
1068760c2415Smrg t1.a = 3;
1069760c2415Smrg t1.b = 4;
1070760c2415Smrg auto t2 = t0.rename!a1;
1071760c2415Smrg t2.a = 3;
1072760c2415Smrg t2.b = 4;
1073760c2415Smrg auto t3 = t0.rename;
1074760c2415Smrg t3.a = 3;
1075760c2415Smrg t3.b = 4;
1076760c2415Smrg }
1077760c2415Smrg
1078760c2415Smrg /**
1079760c2415Smrg * Takes a slice by-reference of this `Tuple`.
1080760c2415Smrg *
1081760c2415Smrg * Params:
1082760c2415Smrg * from = A `size_t` designating the starting position of the slice.
1083760c2415Smrg * to = A `size_t` designating the ending position (exclusive) of the slice.
1084760c2415Smrg *
1085760c2415Smrg * Returns:
1086760c2415Smrg * A new `Tuple` that is a slice from `[from, to$(RPAREN)` of the original.
1087760c2415Smrg * It has the same types and values as the range `[from, to$(RPAREN)` in
1088760c2415Smrg * the original.
1089760c2415Smrg */
1090760c2415Smrg @property
1091760c2415Smrg ref inout(Tuple!(sliceSpecs!(from, to))) slice(size_t from, size_t to)() inout @trusted
1092760c2415Smrg if (from <= to && to <= Types.length)
1093760c2415Smrg {
1094760c2415Smrg static assert(
1095760c2415Smrg (typeof(this).alignof % typeof(return).alignof == 0) &&
1096760c2415Smrg (expand[from].offsetof % typeof(return).alignof == 0),
1097760c2415Smrg "Slicing by reference is impossible because of an alignment mistmatch. (See Phobos issue #15645.)");
1098760c2415Smrg
1099760c2415Smrg return *cast(typeof(return)*) &(field[from]);
1100760c2415Smrg }
1101760c2415Smrg
1102760c2415Smrg ///
1103760c2415Smrg static if (Specs.length == 0) @safe unittest
1104760c2415Smrg {
1105760c2415Smrg Tuple!(int, string, float, double) a;
1106760c2415Smrg a[1] = "abc";
1107760c2415Smrg a[2] = 4.5;
1108760c2415Smrg auto s = a.slice!(1, 3);
1109760c2415Smrg static assert(is(typeof(s) == Tuple!(string, float)));
1110760c2415Smrg assert(s[0] == "abc" && s[1] == 4.5);
1111760c2415Smrg
1112760c2415Smrg // Phobos issue #15645
1113760c2415Smrg Tuple!(int, short, bool, double) b;
1114760c2415Smrg static assert(!__traits(compiles, b.slice!(2, 4)));
1115760c2415Smrg }
1116760c2415Smrg
1117760c2415Smrg /**
1118760c2415Smrg Creates a hash of this `Tuple`.
1119760c2415Smrg
1120760c2415Smrg Returns:
1121760c2415Smrg A `size_t` representing the hash of this `Tuple`.
1122760c2415Smrg */
1123760c2415Smrg size_t toHash() const nothrow @trusted
1124760c2415Smrg {
1125760c2415Smrg size_t h = 0;
1126760c2415Smrg foreach (i, T; Types)
1127760c2415Smrg h += typeid(T).getHash(cast(const void*)&field[i]);
1128760c2415Smrg return h;
1129760c2415Smrg }
1130760c2415Smrg
1131760c2415Smrg ///
1132760c2415Smrg template toString()
1133760c2415Smrg {
1134760c2415Smrg /**
1135760c2415Smrg * Converts to string.
1136760c2415Smrg *
1137760c2415Smrg * Returns:
1138760c2415Smrg * The string representation of this `Tuple`.
1139760c2415Smrg */
1140760c2415Smrg string toString()() const
1141760c2415Smrg {
1142760c2415Smrg import std.array : appender;
1143760c2415Smrg auto app = appender!string();
1144760c2415Smrg this.toString((const(char)[] chunk) => app ~= chunk);
1145760c2415Smrg return app.data;
1146760c2415Smrg }
1147760c2415Smrg
1148760c2415Smrg import std.format : FormatSpec;
1149760c2415Smrg
1150760c2415Smrg /**
1151760c2415Smrg * Formats `Tuple` with either `%s`, `%(inner%)` or `%(inner%|sep%)`.
1152760c2415Smrg *
1153760c2415Smrg * $(TABLE2 Formats supported by Tuple,
1154760c2415Smrg * $(THEAD Format, Description)
1155760c2415Smrg * $(TROW $(P `%s`), $(P Format like `Tuple!(types)(elements formatted with %s each)`.))
1156760c2415Smrg * $(TROW $(P `%(inner%)`), $(P The format `inner` is applied the expanded `Tuple`, so
1157760c2415Smrg * it may contain as many formats as the `Tuple` has fields.))
1158760c2415Smrg * $(TROW $(P `%(inner%|sep%)`), $(P The format `inner` is one format, that is applied
1159760c2415Smrg * on all fields of the `Tuple`. The inner format must be compatible to all
1160760c2415Smrg * of them.)))
1161760c2415Smrg * ---
1162760c2415Smrg * Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];
1163760c2415Smrg *
1164760c2415Smrg * // Default format
1165760c2415Smrg * assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`);
1166760c2415Smrg *
1167760c2415Smrg * // One Format for each individual component
1168760c2415Smrg * assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`);
1169760c2415Smrg * assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`);
1170760c2415Smrg *
1171760c2415Smrg * // One Format for all components
1172760c2415Smrg * assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`);
1173760c2415Smrg *
1174760c2415Smrg * // Array of Tuples
1175760c2415Smrg * assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`);
1176760c2415Smrg *
1177760c2415Smrg *
1178760c2415Smrg * // Error: %( %) missing.
1179760c2415Smrg * assertThrown!FormatException(
1180760c2415Smrg * format("%d, %f", tuple(1, 2.0)) == `1, 2.0`
1181760c2415Smrg * );
1182760c2415Smrg *
1183760c2415Smrg * // Error: %( %| %) missing.
1184760c2415Smrg * assertThrown!FormatException(
1185760c2415Smrg * format("%d", tuple(1, 2)) == `1, 2`
1186760c2415Smrg * );
1187760c2415Smrg *
1188760c2415Smrg * // Error: %d inadequate for double.
1189760c2415Smrg * assertThrown!FormatException(
1190760c2415Smrg * format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0`
1191760c2415Smrg * );
1192760c2415Smrg * ---
1193760c2415Smrg */
1194760c2415Smrg void toString(DG)(scope DG sink) const
1195760c2415Smrg {
1196760c2415Smrg toString(sink, FormatSpec!char());
1197760c2415Smrg }
1198760c2415Smrg
1199760c2415Smrg /// ditto
1200760c2415Smrg void toString(DG, Char)(scope DG sink, FormatSpec!Char fmt) const
1201760c2415Smrg {
1202760c2415Smrg import std.format : formatElement, formattedWrite, FormatException;
1203760c2415Smrg if (fmt.nested)
1204760c2415Smrg {
1205760c2415Smrg if (fmt.sep)
1206760c2415Smrg {
1207760c2415Smrg foreach (i, Type; Types)
1208760c2415Smrg {
1209760c2415Smrg static if (i > 0)
1210760c2415Smrg {
1211760c2415Smrg sink(fmt.sep);
1212760c2415Smrg }
1213760c2415Smrg // TODO: Change this once formattedWrite() works for shared objects.
1214760c2415Smrg static if (is(Type == class) && is(Type == shared))
1215760c2415Smrg {
1216760c2415Smrg sink(Type.stringof);
1217760c2415Smrg }
1218760c2415Smrg else
1219760c2415Smrg {
1220760c2415Smrg formattedWrite(sink, fmt.nested, this.field[i]);
1221760c2415Smrg }
1222760c2415Smrg }
1223760c2415Smrg }
1224760c2415Smrg else
1225760c2415Smrg {
1226760c2415Smrg formattedWrite(sink, fmt.nested, staticMap!(sharedToString, this.expand));
1227760c2415Smrg }
1228760c2415Smrg }
1229760c2415Smrg else if (fmt.spec == 's')
1230760c2415Smrg {
1231760c2415Smrg enum header = Unqual!(typeof(this)).stringof ~ "(",
1232760c2415Smrg footer = ")",
1233760c2415Smrg separator = ", ";
1234760c2415Smrg sink(header);
1235760c2415Smrg foreach (i, Type; Types)
1236760c2415Smrg {
1237760c2415Smrg static if (i > 0)
1238760c2415Smrg {
1239760c2415Smrg sink(separator);
1240760c2415Smrg }
1241760c2415Smrg // TODO: Change this once formatElement() works for shared objects.
1242760c2415Smrg static if (is(Type == class) && is(Type == shared))
1243760c2415Smrg {
1244760c2415Smrg sink(Type.stringof);
1245760c2415Smrg }
1246760c2415Smrg else
1247760c2415Smrg {
1248760c2415Smrg FormatSpec!Char f;
1249760c2415Smrg formatElement(sink, field[i], f);
1250760c2415Smrg }
1251760c2415Smrg }
1252760c2415Smrg sink(footer);
1253760c2415Smrg }
1254760c2415Smrg else
1255760c2415Smrg {
1256760c2415Smrg throw new FormatException(
1257760c2415Smrg "Expected '%s' or '%(...%)' or '%(...%|...%)' format specifier for type '" ~
1258760c2415Smrg Unqual!(typeof(this)).stringof ~ "', not '%" ~ fmt.spec ~ "'.");
1259760c2415Smrg }
1260760c2415Smrg }
1261760c2415Smrg }
1262760c2415Smrg }
1263760c2415Smrg }
1264760c2415Smrg
1265760c2415Smrg ///
1266760c2415Smrg @safe unittest
1267760c2415Smrg {
1268760c2415Smrg Tuple!(int, int) point;
1269760c2415Smrg // assign coordinates
1270760c2415Smrg point[0] = 5;
1271760c2415Smrg point[1] = 6;
1272760c2415Smrg // read coordinates
1273760c2415Smrg auto x = point[0];
1274760c2415Smrg auto y = point[1];
1275760c2415Smrg }
1276760c2415Smrg
1277760c2415Smrg /**
1278760c2415Smrg `Tuple` members can be named. It is legal to mix named and unnamed
1279760c2415Smrg members. The method above is still applicable to all fields.
1280760c2415Smrg */
1281760c2415Smrg @safe unittest
1282760c2415Smrg {
1283760c2415Smrg alias Entry = Tuple!(int, "index", string, "value");
1284760c2415Smrg Entry e;
1285760c2415Smrg e.index = 4;
1286760c2415Smrg e.value = "Hello";
1287760c2415Smrg assert(e[1] == "Hello");
1288760c2415Smrg assert(e[0] == 4);
1289760c2415Smrg }
1290760c2415Smrg
1291760c2415Smrg /**
1292760c2415Smrg A `Tuple` with named fields is a distinct type from a `Tuple` with unnamed
1293760c2415Smrg fields, i.e. each naming imparts a separate type for the `Tuple`. Two
1294760c2415Smrg `Tuple`s differing in naming only are still distinct, even though they
1295760c2415Smrg might have the same structure.
1296760c2415Smrg */
1297760c2415Smrg @safe unittest
1298760c2415Smrg {
1299760c2415Smrg Tuple!(int, "x", int, "y") point1;
1300760c2415Smrg Tuple!(int, int) point2;
1301760c2415Smrg assert(!is(typeof(point1) == typeof(point2)));
1302760c2415Smrg }
1303760c2415Smrg
1304760c2415Smrg /**
1305760c2415Smrg Creates a copy of a $(LREF Tuple) with its fields in _reverse order.
1306760c2415Smrg
1307760c2415Smrg Params:
1308760c2415Smrg t = The `Tuple` to copy.
1309760c2415Smrg
1310760c2415Smrg Returns:
1311760c2415Smrg A new `Tuple`.
1312760c2415Smrg */
1313760c2415Smrg auto reverse(T)(T t)
1314760c2415Smrg if (isTuple!T)
1315760c2415Smrg {
1316760c2415Smrg import std.meta : Reverse;
1317760c2415Smrg // @@@BUG@@@ Cannot be an internal function due to forward reference issues.
1318760c2415Smrg
1319760c2415Smrg // @@@BUG@@@ 9929 Need 'this' when calling template with expanded tuple
1320760c2415Smrg // return tuple(Reverse!(t.expand));
1321760c2415Smrg
1322760c2415Smrg ReverseTupleType!T result;
1323760c2415Smrg auto tup = t.expand;
1324760c2415Smrg result.expand = Reverse!tup;
1325760c2415Smrg return result;
1326760c2415Smrg }
1327760c2415Smrg
1328760c2415Smrg ///
1329760c2415Smrg @safe unittest
1330760c2415Smrg {
1331760c2415Smrg auto tup = tuple(1, "2");
1332760c2415Smrg assert(tup.reverse == tuple("2", 1));
1333760c2415Smrg }
1334760c2415Smrg
1335760c2415Smrg /* Get a Tuple type with the reverse specification of Tuple T. */
1336760c2415Smrg private template ReverseTupleType(T)
1337760c2415Smrg if (isTuple!T)
1338760c2415Smrg {
1339760c2415Smrg static if (is(T : Tuple!A, A...))
1340760c2415Smrg alias ReverseTupleType = Tuple!(ReverseTupleSpecs!A);
1341760c2415Smrg }
1342760c2415Smrg
1343760c2415Smrg /* Reverse the Specs of a Tuple. */
1344760c2415Smrg private template ReverseTupleSpecs(T...)
1345760c2415Smrg {
1346760c2415Smrg static if (T.length > 1)
1347760c2415Smrg {
1348760c2415Smrg static if (is(typeof(T[$-1]) : string))
1349760c2415Smrg {
1350760c2415Smrg alias ReverseTupleSpecs = AliasSeq!(T[$-2], T[$-1], ReverseTupleSpecs!(T[0 .. $-2]));
1351760c2415Smrg }
1352760c2415Smrg else
1353760c2415Smrg {
1354760c2415Smrg alias ReverseTupleSpecs = AliasSeq!(T[$-1], ReverseTupleSpecs!(T[0 .. $-1]));
1355760c2415Smrg }
1356760c2415Smrg }
1357760c2415Smrg else
1358760c2415Smrg {
1359760c2415Smrg alias ReverseTupleSpecs = T;
1360760c2415Smrg }
1361760c2415Smrg }
1362760c2415Smrg
1363760c2415Smrg // ensure that internal Tuple unittests are compiled
1364760c2415Smrg @safe unittest
1365760c2415Smrg {
1366760c2415Smrg Tuple!() t;
1367760c2415Smrg }
1368760c2415Smrg
1369760c2415Smrg @safe unittest
1370760c2415Smrg {
1371760c2415Smrg import std.conv;
1372760c2415Smrg {
1373760c2415Smrg Tuple!(int, "a", int, "b") nosh;
1374760c2415Smrg static assert(nosh.length == 2);
1375760c2415Smrg nosh.a = 5;
1376760c2415Smrg nosh.b = 6;
1377760c2415Smrg assert(nosh.a == 5);
1378760c2415Smrg assert(nosh.b == 6);
1379760c2415Smrg }
1380760c2415Smrg {
1381760c2415Smrg Tuple!(short, double) b;
1382760c2415Smrg static assert(b.length == 2);
1383760c2415Smrg b[1] = 5;
1384760c2415Smrg auto a = Tuple!(int, real)(b);
1385760c2415Smrg assert(a[0] == 0 && a[1] == 5);
1386760c2415Smrg a = Tuple!(int, real)(1, 2);
1387760c2415Smrg assert(a[0] == 1 && a[1] == 2);
1388760c2415Smrg auto c = Tuple!(int, "a", double, "b")(a);
1389760c2415Smrg assert(c[0] == 1 && c[1] == 2);
1390760c2415Smrg }
1391760c2415Smrg {
1392760c2415Smrg Tuple!(int, real) nosh;
1393760c2415Smrg nosh[0] = 5;
1394760c2415Smrg nosh[1] = 0;
1395760c2415Smrg assert(nosh[0] == 5 && nosh[1] == 0);
1396760c2415Smrg assert(nosh.to!string == "Tuple!(int, real)(5, 0)", nosh.to!string);
1397760c2415Smrg Tuple!(int, int) yessh;
1398760c2415Smrg nosh = yessh;
1399760c2415Smrg }
1400760c2415Smrg {
1401760c2415Smrg class A {}
1402760c2415Smrg Tuple!(int, shared A) nosh;
1403760c2415Smrg nosh[0] = 5;
1404760c2415Smrg assert(nosh[0] == 5 && nosh[1] is null);
1405760c2415Smrg assert(nosh.to!string == "Tuple!(int, shared(A))(5, shared(A))");
1406760c2415Smrg }
1407760c2415Smrg {
1408760c2415Smrg Tuple!(int, string) t;
1409760c2415Smrg t[0] = 10;
1410760c2415Smrg t[1] = "str";
1411760c2415Smrg assert(t[0] == 10 && t[1] == "str");
1412760c2415Smrg assert(t.to!string == `Tuple!(int, string)(10, "str")`, t.to!string);
1413760c2415Smrg }
1414760c2415Smrg {
1415760c2415Smrg Tuple!(int, "a", double, "b") x;
1416760c2415Smrg static assert(x.a.offsetof == x[0].offsetof);
1417760c2415Smrg static assert(x.b.offsetof == x[1].offsetof);
1418760c2415Smrg x.b = 4.5;
1419760c2415Smrg x.a = 5;
1420760c2415Smrg assert(x[0] == 5 && x[1] == 4.5);
1421760c2415Smrg assert(x.a == 5 && x.b == 4.5);
1422760c2415Smrg }
1423760c2415Smrg // indexing
1424760c2415Smrg {
1425760c2415Smrg Tuple!(int, real) t;
1426760c2415Smrg static assert(is(typeof(t[0]) == int));
1427760c2415Smrg static assert(is(typeof(t[1]) == real));
1428760c2415Smrg int* p0 = &t[0];
1429760c2415Smrg real* p1 = &t[1];
1430760c2415Smrg t[0] = 10;
1431760c2415Smrg t[1] = -200.0L;
1432760c2415Smrg assert(*p0 == t[0]);
1433760c2415Smrg assert(*p1 == t[1]);
1434760c2415Smrg }
1435760c2415Smrg // slicing
1436760c2415Smrg {
1437760c2415Smrg Tuple!(int, "x", real, "y", double, "z", string) t;
1438760c2415Smrg t[0] = 10;
1439760c2415Smrg t[1] = 11;
1440760c2415Smrg t[2] = 12;
1441760c2415Smrg t[3] = "abc";
1442760c2415Smrg auto a = t.slice!(0, 3);
1443760c2415Smrg assert(a.length == 3);
1444760c2415Smrg assert(a.x == t.x);
1445760c2415Smrg assert(a.y == t.y);
1446760c2415Smrg assert(a.z == t.z);
1447760c2415Smrg auto b = t.slice!(2, 4);
1448760c2415Smrg assert(b.length == 2);
1449760c2415Smrg assert(b.z == t.z);
1450760c2415Smrg assert(b[1] == t[3]);
1451760c2415Smrg }
1452760c2415Smrg // nesting
1453760c2415Smrg {
1454760c2415Smrg Tuple!(Tuple!(int, real), Tuple!(string, "s")) t;
1455760c2415Smrg static assert(is(typeof(t[0]) == Tuple!(int, real)));
1456760c2415Smrg static assert(is(typeof(t[1]) == Tuple!(string, "s")));
1457760c2415Smrg static assert(is(typeof(t[0][0]) == int));
1458760c2415Smrg static assert(is(typeof(t[0][1]) == real));
1459760c2415Smrg static assert(is(typeof(t[1].s) == string));
1460760c2415Smrg t[0] = tuple(10, 20.0L);
1461760c2415Smrg t[1].s = "abc";
1462760c2415Smrg assert(t[0][0] == 10);
1463760c2415Smrg assert(t[0][1] == 20.0L);
1464760c2415Smrg assert(t[1].s == "abc");
1465760c2415Smrg }
1466760c2415Smrg // non-POD
1467760c2415Smrg {
1468760c2415Smrg static struct S
1469760c2415Smrg {
1470760c2415Smrg int count;
1471760c2415Smrg this(this) { ++count; }
1472760c2415Smrg ~this() { --count; }
1473760c2415Smrg void opAssign(S rhs) { count = rhs.count; }
1474760c2415Smrg }
1475760c2415Smrg Tuple!(S, S) ss;
1476760c2415Smrg Tuple!(S, S) ssCopy = ss;
1477760c2415Smrg assert(ssCopy[0].count == 1);
1478760c2415Smrg assert(ssCopy[1].count == 1);
1479760c2415Smrg ssCopy[1] = ssCopy[0];
1480760c2415Smrg assert(ssCopy[1].count == 2);
1481760c2415Smrg }
1482760c2415Smrg // bug 2800
1483760c2415Smrg {
1484760c2415Smrg static struct R
1485760c2415Smrg {
1486760c2415Smrg Tuple!(int, int) _front;
1487760c2415Smrg @property ref Tuple!(int, int) front() return { return _front; }
1488760c2415Smrg @property bool empty() { return _front[0] >= 10; }
1489760c2415Smrg void popFront() { ++_front[0]; }
1490760c2415Smrg }
1491760c2415Smrg foreach (a; R())
1492760c2415Smrg {
1493760c2415Smrg static assert(is(typeof(a) == Tuple!(int, int)));
1494760c2415Smrg assert(0 <= a[0] && a[0] < 10);
1495760c2415Smrg assert(a[1] == 0);
1496760c2415Smrg }
1497760c2415Smrg }
1498760c2415Smrg // Construction with compatible elements
1499760c2415Smrg {
1500760c2415Smrg auto t1 = Tuple!(int, double)(1, 1);
1501760c2415Smrg
1502760c2415Smrg // 8702
1503760c2415Smrg auto t8702a = tuple(tuple(1));
1504760c2415Smrg auto t8702b = Tuple!(Tuple!(int))(Tuple!(int)(1));
1505760c2415Smrg }
1506760c2415Smrg // Construction with compatible tuple
1507760c2415Smrg {
1508760c2415Smrg Tuple!(int, int) x;
1509760c2415Smrg x[0] = 10;
1510760c2415Smrg x[1] = 20;
1511760c2415Smrg Tuple!(int, "a", double, "b") y = x;
1512760c2415Smrg assert(y.a == 10);
1513760c2415Smrg assert(y.b == 20);
1514760c2415Smrg // incompatible
1515760c2415Smrg static assert(!__traits(compiles, Tuple!(int, int)(y)));
1516760c2415Smrg }
1517760c2415Smrg // 6275
1518760c2415Smrg {
1519760c2415Smrg const int x = 1;
1520760c2415Smrg auto t1 = tuple(x);
1521760c2415Smrg alias T = Tuple!(const(int));
1522760c2415Smrg auto t2 = T(1);
1523760c2415Smrg }
1524760c2415Smrg // 9431
1525760c2415Smrg {
1526760c2415Smrg alias T = Tuple!(int[1][]);
1527760c2415Smrg auto t = T([[10]]);
1528760c2415Smrg }
1529760c2415Smrg // 7666
1530760c2415Smrg {
1531760c2415Smrg auto tup = tuple(1, "2");
1532760c2415Smrg assert(tup.reverse == tuple("2", 1));
1533760c2415Smrg }
1534760c2415Smrg {
1535760c2415Smrg Tuple!(int, "x", string, "y") tup = tuple(1, "2");
1536760c2415Smrg auto rev = tup.reverse;
1537760c2415Smrg assert(rev == tuple("2", 1));
1538760c2415Smrg assert(rev.x == 1 && rev.y == "2");
1539760c2415Smrg }
1540760c2415Smrg {
1541760c2415Smrg Tuple!(wchar, dchar, int, "x", string, "y", char, byte, float) tup;
1542760c2415Smrg tup = tuple('a', 'b', 3, "4", 'c', cast(byte) 0x0D, 0.00);
1543760c2415Smrg auto rev = tup.reverse;
1544760c2415Smrg assert(rev == tuple(0.00, cast(byte) 0x0D, 'c', "4", 3, 'b', 'a'));
1545760c2415Smrg assert(rev.x == 3 && rev.y == "4");
1546760c2415Smrg }
1547760c2415Smrg }
1548760c2415Smrg @safe unittest
1549760c2415Smrg {
1550760c2415Smrg // opEquals
1551760c2415Smrg {
1552760c2415Smrg struct Equ1 { bool opEquals(Equ1) { return true; } }
1553760c2415Smrg auto tm1 = tuple(Equ1.init);
1554760c2415Smrg const tc1 = tuple(Equ1.init);
1555760c2415Smrg static assert( is(typeof(tm1 == tm1)));
1556760c2415Smrg static assert(!is(typeof(tm1 == tc1)));
1557760c2415Smrg static assert(!is(typeof(tc1 == tm1)));
1558760c2415Smrg static assert(!is(typeof(tc1 == tc1)));
1559760c2415Smrg
1560760c2415Smrg struct Equ2 { bool opEquals(const Equ2) const { return true; } }
1561760c2415Smrg auto tm2 = tuple(Equ2.init);
1562760c2415Smrg const tc2 = tuple(Equ2.init);
1563760c2415Smrg static assert( is(typeof(tm2 == tm2)));
1564760c2415Smrg static assert( is(typeof(tm2 == tc2)));
1565760c2415Smrg static assert( is(typeof(tc2 == tm2)));
1566760c2415Smrg static assert( is(typeof(tc2 == tc2)));
1567760c2415Smrg
1568760c2415Smrg struct Equ3 { bool opEquals(T)(T) { return true; } }
1569760c2415Smrg auto tm3 = tuple(Equ3.init); // bugzilla 8686
1570760c2415Smrg const tc3 = tuple(Equ3.init);
1571760c2415Smrg static assert( is(typeof(tm3 == tm3)));
1572760c2415Smrg static assert( is(typeof(tm3 == tc3)));
1573760c2415Smrg static assert(!is(typeof(tc3 == tm3)));
1574760c2415Smrg static assert(!is(typeof(tc3 == tc3)));
1575760c2415Smrg
1576760c2415Smrg struct Equ4 { bool opEquals(T)(T) const { return true; } }
1577760c2415Smrg auto tm4 = tuple(Equ4.init);
1578760c2415Smrg const tc4 = tuple(Equ4.init);
1579760c2415Smrg static assert( is(typeof(tm4 == tm4)));
1580760c2415Smrg static assert( is(typeof(tm4 == tc4)));
1581760c2415Smrg static assert( is(typeof(tc4 == tm4)));
1582760c2415Smrg static assert( is(typeof(tc4 == tc4)));
1583760c2415Smrg }
1584760c2415Smrg // opCmp
1585760c2415Smrg {
1586760c2415Smrg struct Cmp1 { int opCmp(Cmp1) { return 0; } }
1587760c2415Smrg auto tm1 = tuple(Cmp1.init);
1588760c2415Smrg const tc1 = tuple(Cmp1.init);
1589760c2415Smrg static assert( is(typeof(tm1 < tm1)));
1590760c2415Smrg static assert(!is(typeof(tm1 < tc1)));
1591760c2415Smrg static assert(!is(typeof(tc1 < tm1)));
1592760c2415Smrg static assert(!is(typeof(tc1 < tc1)));
1593760c2415Smrg
1594760c2415Smrg struct Cmp2 { int opCmp(const Cmp2) const { return 0; } }
1595760c2415Smrg auto tm2 = tuple(Cmp2.init);
1596760c2415Smrg const tc2 = tuple(Cmp2.init);
1597760c2415Smrg static assert( is(typeof(tm2 < tm2)));
1598760c2415Smrg static assert( is(typeof(tm2 < tc2)));
1599760c2415Smrg static assert( is(typeof(tc2 < tm2)));
1600760c2415Smrg static assert( is(typeof(tc2 < tc2)));
1601760c2415Smrg
1602760c2415Smrg struct Cmp3 { int opCmp(T)(T) { return 0; } }
1603760c2415Smrg auto tm3 = tuple(Cmp3.init);
1604760c2415Smrg const tc3 = tuple(Cmp3.init);
1605760c2415Smrg static assert( is(typeof(tm3 < tm3)));
1606760c2415Smrg static assert( is(typeof(tm3 < tc3)));
1607760c2415Smrg static assert(!is(typeof(tc3 < tm3)));
1608760c2415Smrg static assert(!is(typeof(tc3 < tc3)));
1609760c2415Smrg
1610760c2415Smrg struct Cmp4 { int opCmp(T)(T) const { return 0; } }
1611760c2415Smrg auto tm4 = tuple(Cmp4.init);
1612760c2415Smrg const tc4 = tuple(Cmp4.init);
1613760c2415Smrg static assert( is(typeof(tm4 < tm4)));
1614760c2415Smrg static assert( is(typeof(tm4 < tc4)));
1615760c2415Smrg static assert( is(typeof(tc4 < tm4)));
1616760c2415Smrg static assert( is(typeof(tc4 < tc4)));
1617760c2415Smrg }
1618760c2415Smrg // Bugzilla 14890
1619760c2415Smrg static void test14890(inout int[] dummy)
1620760c2415Smrg {
1621760c2415Smrg alias V = Tuple!(int, int);
1622760c2415Smrg
1623760c2415Smrg V mv;
1624760c2415Smrg const V cv;
1625760c2415Smrg immutable V iv;
1626760c2415Smrg inout V wv; // OK <- NG
1627760c2415Smrg inout const V wcv; // OK <- NG
1628760c2415Smrg
1629760c2415Smrg foreach (v1; AliasSeq!(mv, cv, iv, wv, wcv))
1630760c2415Smrg foreach (v2; AliasSeq!(mv, cv, iv, wv, wcv))
1631760c2415Smrg {
1632760c2415Smrg assert(!(v1 < v2));
1633760c2415Smrg }
1634760c2415Smrg }
1635760c2415Smrg {
1636760c2415Smrg int[2] ints = [ 1, 2 ];
1637760c2415Smrg Tuple!(int, int) t = ints;
1638760c2415Smrg assert(t[0] == 1 && t[1] == 2);
1639760c2415Smrg Tuple!(long, uint) t2 = ints;
1640760c2415Smrg assert(t2[0] == 1 && t2[1] == 2);
1641760c2415Smrg }
1642760c2415Smrg }
1643760c2415Smrg @safe unittest
1644760c2415Smrg {
1645760c2415Smrg auto t1 = Tuple!(int, "x", string, "y")(1, "a");
1646760c2415Smrg assert(t1.x == 1);
1647760c2415Smrg assert(t1.y == "a");
1648760c2415Smrg void foo(Tuple!(int, string) t2) {}
1649760c2415Smrg foo(t1);
1650760c2415Smrg
1651760c2415Smrg Tuple!(int, int)[] arr;
1652760c2415Smrg arr ~= tuple(10, 20); // OK
1653760c2415Smrg arr ~= Tuple!(int, "x", int, "y")(10, 20); // NG -> OK
1654760c2415Smrg
1655760c2415Smrg static assert(is(typeof(Tuple!(int, "x", string, "y").tupleof) ==
1656760c2415Smrg typeof(Tuple!(int, string ).tupleof)));
1657760c2415Smrg }
1658760c2415Smrg @safe unittest
1659760c2415Smrg {
1660760c2415Smrg // Bugzilla 10686
1661760c2415Smrg immutable Tuple!(int) t1;
1662760c2415Smrg auto r1 = t1[0]; // OK
1663760c2415Smrg immutable Tuple!(int, "x") t2;
1664760c2415Smrg auto r2 = t2[0]; // error
1665760c2415Smrg }
1666760c2415Smrg @safe unittest
1667760c2415Smrg {
1668760c2415Smrg import std.exception : assertCTFEable;
1669760c2415Smrg
1670760c2415Smrg // Bugzilla 10218
1671760c2415Smrg assertCTFEable!(
1672760c2415Smrg {
1673760c2415Smrg auto t = tuple(1);
1674760c2415Smrg t = tuple(2); // assignment
1675760c2415Smrg });
1676760c2415Smrg }
1677760c2415Smrg @safe unittest
1678760c2415Smrg {
1679760c2415Smrg class Foo{}
1680760c2415Smrg Tuple!(immutable(Foo)[]) a;
1681760c2415Smrg }
1682760c2415Smrg
1683760c2415Smrg @safe unittest
1684760c2415Smrg {
1685760c2415Smrg //Test non-assignable
1686760c2415Smrg static struct S
1687760c2415Smrg {
1688760c2415Smrg int* p;
1689760c2415Smrg }
1690760c2415Smrg alias IS = immutable S;
1691760c2415Smrg static assert(!isAssignable!IS);
1692760c2415Smrg
1693760c2415Smrg auto s = IS.init;
1694760c2415Smrg
1695760c2415Smrg alias TIS = Tuple!IS;
1696760c2415Smrg TIS a = tuple(s);
1697760c2415Smrg TIS b = a;
1698760c2415Smrg
1699760c2415Smrg alias TISIS = Tuple!(IS, IS);
1700760c2415Smrg TISIS d = tuple(s, s);
1701760c2415Smrg IS[2] ss;
1702760c2415Smrg TISIS e = TISIS(ss);
1703760c2415Smrg }
1704760c2415Smrg
1705760c2415Smrg // Bugzilla #9819
1706760c2415Smrg @safe unittest
1707760c2415Smrg {
1708760c2415Smrg alias T = Tuple!(int, "x", double, "foo");
1709760c2415Smrg static assert(T.fieldNames[0] == "x");
1710760c2415Smrg static assert(T.fieldNames[1] == "foo");
1711760c2415Smrg
1712760c2415Smrg alias Fields = Tuple!(int, "id", string, float);
1713760c2415Smrg static assert(Fields.fieldNames == AliasSeq!("id", "", ""));
1714760c2415Smrg }
1715760c2415Smrg
1716760c2415Smrg // Bugzilla 13837
1717760c2415Smrg @safe unittest
1718760c2415Smrg {
1719760c2415Smrg // New behaviour, named arguments.
1720760c2415Smrg static assert(is(
1721760c2415Smrg typeof(tuple!("x")(1)) == Tuple!(int, "x")));
1722760c2415Smrg static assert(is(
1723760c2415Smrg typeof(tuple!("x")(1.0)) == Tuple!(double, "x")));
1724760c2415Smrg static assert(is(
1725760c2415Smrg typeof(tuple!("x")("foo")) == Tuple!(string, "x")));
1726760c2415Smrg static assert(is(
1727760c2415Smrg typeof(tuple!("x", "y")(1, 2.0)) == Tuple!(int, "x", double, "y")));
1728760c2415Smrg
1729760c2415Smrg auto a = tuple!("a", "b", "c")("1", 2, 3.0f);
1730760c2415Smrg static assert(is(typeof(a.a) == string));
1731760c2415Smrg static assert(is(typeof(a.b) == int));
1732760c2415Smrg static assert(is(typeof(a.c) == float));
1733760c2415Smrg
1734760c2415Smrg // Old behaviour, but with explicit type parameters.
1735760c2415Smrg static assert(is(
1736760c2415Smrg typeof(tuple!(int, double)(1, 2.0)) == Tuple!(int, double)));
1737760c2415Smrg static assert(is(
1738760c2415Smrg typeof(tuple!(const int)(1)) == Tuple!(const int)));
1739760c2415Smrg static assert(is(
1740760c2415Smrg typeof(tuple()) == Tuple!()));
1741760c2415Smrg
1742760c2415Smrg // Nonsensical behaviour
1743760c2415Smrg static assert(!__traits(compiles, tuple!(1)(2)));
1744760c2415Smrg static assert(!__traits(compiles, tuple!("x")(1, 2)));
1745760c2415Smrg static assert(!__traits(compiles, tuple!("x", "y")(1)));
1746760c2415Smrg static assert(!__traits(compiles, tuple!("x")()));
1747760c2415Smrg static assert(!__traits(compiles, tuple!("x", int)(2)));
1748760c2415Smrg }
1749760c2415Smrg
1750760c2415Smrg @safe unittest
1751760c2415Smrg {
1752760c2415Smrg class C {}
1753760c2415Smrg Tuple!(Rebindable!(const C)) a;
1754760c2415Smrg Tuple!(const C) b;
1755760c2415Smrg a = b;
1756760c2415Smrg }
1757760c2415Smrg
1758760c2415Smrg @nogc @safe unittest
1759760c2415Smrg {
1760760c2415Smrg alias T = Tuple!(string, "s");
1761760c2415Smrg T x;
1762760c2415Smrg x = T.init;
1763760c2415Smrg }
1764760c2415Smrg
1765760c2415Smrg @safe unittest
1766760c2415Smrg {
1767760c2415Smrg import std.format : format, FormatException;
1768760c2415Smrg import std.exception : assertThrown;
1769760c2415Smrg
1770760c2415Smrg // enum tupStr = tuple(1, 1.0).toString; // toString is *impure*.
1771760c2415Smrg //static assert(tupStr == `Tuple!(int, double)(1, 1)`);
1772760c2415Smrg
1773760c2415Smrg Tuple!(int, double)[3] tupList = [ tuple(1, 1.0), tuple(2, 4.0), tuple(3, 9.0) ];
1774760c2415Smrg
1775760c2415Smrg // Default format
1776760c2415Smrg assert(format("%s", tuple("a", 1)) == `Tuple!(string, int)("a", 1)`);
1777760c2415Smrg
1778760c2415Smrg // One Format for each individual component
1779760c2415Smrg assert(format("%(%#x v %.4f w %#x%)", tuple(1, 1.0, 10)) == `0x1 v 1.0000 w 0xa`);
1780760c2415Smrg assert(format( "%#x v %.4f w %#x" , tuple(1, 1.0, 10).expand) == `0x1 v 1.0000 w 0xa`);
1781760c2415Smrg
1782760c2415Smrg // One Format for all components
1783760c2415Smrg assert(format("%(>%s<%| & %)", tuple("abc", 1, 2.3, [4, 5])) == `>abc< & >1< & >2.3< & >[4, 5]<`);
1784760c2415Smrg
1785760c2415Smrg // Array of Tuples
1786760c2415Smrg assert(format("%(%(f(%d) = %.1f%); %)", tupList) == `f(1) = 1.0; f(2) = 4.0; f(3) = 9.0`);
1787760c2415Smrg
1788760c2415Smrg
1789760c2415Smrg // Error: %( %) missing.
1790760c2415Smrg assertThrown!FormatException(
1791760c2415Smrg format("%d, %f", tuple(1, 2.0)) == `1, 2.0`
1792760c2415Smrg );
1793760c2415Smrg
1794760c2415Smrg // Error: %( %| %) missing.
1795760c2415Smrg assertThrown!FormatException(
1796760c2415Smrg format("%d", tuple(1, 2)) == `1, 2`
1797760c2415Smrg );
1798760c2415Smrg
1799760c2415Smrg // Error: %d inadequate for double
1800760c2415Smrg assertThrown!FormatException(
1801760c2415Smrg format("%(%d%|, %)", tuple(1, 2.0)) == `1, 2.0`
1802760c2415Smrg );
1803760c2415Smrg }
1804760c2415Smrg
1805760c2415Smrg /**
1806760c2415Smrg Constructs a $(LREF Tuple) object instantiated and initialized according to
1807760c2415Smrg the given arguments.
1808760c2415Smrg
1809760c2415Smrg Params:
1810760c2415Smrg Names = An optional list of strings naming each successive field of the `Tuple`.
1811760c2415Smrg Each name matches up with the corresponding field given by `Args`.
1812760c2415Smrg A name does not have to be provided for every field, but as
1813760c2415Smrg the names must proceed in order, it is not possible to skip
1814760c2415Smrg one field and name the next after it.
1815760c2415Smrg */
1816760c2415Smrg template tuple(Names...)
1817760c2415Smrg {
1818760c2415Smrg /**
1819760c2415Smrg Params:
1820760c2415Smrg args = Values to initialize the `Tuple` with. The `Tuple`'s type will
1821760c2415Smrg be inferred from the types of the values given.
1822760c2415Smrg
1823760c2415Smrg Returns:
1824760c2415Smrg A new `Tuple` with its type inferred from the arguments given.
1825760c2415Smrg */
1826760c2415Smrg auto tuple(Args...)(Args args)
1827760c2415Smrg {
1828760c2415Smrg static if (Names.length == 0)
1829760c2415Smrg {
1830760c2415Smrg // No specified names, just infer types from Args...
1831760c2415Smrg return Tuple!Args(args);
1832760c2415Smrg }
1833760c2415Smrg else static if (!is(typeof(Names[0]) : string))
1834760c2415Smrg {
1835760c2415Smrg // Names[0] isn't a string, must be explicit types.
1836760c2415Smrg return Tuple!Names(args);
1837760c2415Smrg }
1838760c2415Smrg else
1839760c2415Smrg {
1840760c2415Smrg // Names[0] is a string, so must be specifying names.
1841760c2415Smrg static assert(Names.length == Args.length,
1842760c2415Smrg "Insufficient number of names given.");
1843760c2415Smrg
1844760c2415Smrg // Interleave(a, b).and(c, d) == (a, c, b, d)
1845760c2415Smrg // This is to get the interleaving of types and names for Tuple
1846760c2415Smrg // e.g. Tuple!(int, "x", string, "y")
1847760c2415Smrg template Interleave(A...)
1848760c2415Smrg {
1849760c2415Smrg template and(B...) if (B.length == 1)
1850760c2415Smrg {
1851760c2415Smrg alias and = AliasSeq!(A[0], B[0]);
1852760c2415Smrg }
1853760c2415Smrg
1854760c2415Smrg template and(B...) if (B.length != 1)
1855760c2415Smrg {
1856760c2415Smrg alias and = AliasSeq!(A[0], B[0],
1857760c2415Smrg Interleave!(A[1..$]).and!(B[1..$]));
1858760c2415Smrg }
1859760c2415Smrg }
1860760c2415Smrg return Tuple!(Interleave!(Args).and!(Names))(args);
1861760c2415Smrg }
1862760c2415Smrg }
1863760c2415Smrg }
1864760c2415Smrg
1865760c2415Smrg ///
1866760c2415Smrg @safe unittest
1867760c2415Smrg {
1868760c2415Smrg auto value = tuple(5, 6.7, "hello");
1869760c2415Smrg assert(value[0] == 5);
1870760c2415Smrg assert(value[1] == 6.7);
1871760c2415Smrg assert(value[2] == "hello");
1872760c2415Smrg
1873760c2415Smrg // Field names can be provided.
1874760c2415Smrg auto entry = tuple!("index", "value")(4, "Hello");
1875760c2415Smrg assert(entry.index == 4);
1876760c2415Smrg assert(entry.value == "Hello");
1877760c2415Smrg }
1878760c2415Smrg
1879760c2415Smrg /**
1880760c2415Smrg Returns $(D true) if and only if $(D T) is an instance of $(D std.typecons.Tuple).
1881760c2415Smrg
1882760c2415Smrg Params:
1883760c2415Smrg T = The type to check.
1884760c2415Smrg
1885760c2415Smrg Returns:
1886760c2415Smrg true if `T` is a `Tuple` type, false otherwise.
1887760c2415Smrg */
1888760c2415Smrg enum isTuple(T) = __traits(compiles,
1889760c2415Smrg {
1890760c2415Smrg void f(Specs...)(Tuple!Specs tup) {}
1891760c2415Smrg f(T.init);
1892760c2415Smrg } );
1893760c2415Smrg
1894760c2415Smrg ///
1895760c2415Smrg @safe unittest
1896760c2415Smrg {
1897760c2415Smrg static assert(isTuple!(Tuple!()));
1898760c2415Smrg static assert(isTuple!(Tuple!(int)));
1899760c2415Smrg static assert(isTuple!(Tuple!(int, real, string)));
1900760c2415Smrg static assert(isTuple!(Tuple!(int, "x", real, "y")));
1901760c2415Smrg static assert(isTuple!(Tuple!(int, Tuple!(real), string)));
1902760c2415Smrg }
1903760c2415Smrg
1904760c2415Smrg @safe unittest
1905760c2415Smrg {
1906760c2415Smrg static assert(isTuple!(const Tuple!(int)));
1907760c2415Smrg static assert(isTuple!(immutable Tuple!(int)));
1908760c2415Smrg
1909760c2415Smrg static assert(!isTuple!(int));
1910760c2415Smrg static assert(!isTuple!(const int));
1911760c2415Smrg
1912760c2415Smrg struct S {}
1913760c2415Smrg static assert(!isTuple!(S));
1914760c2415Smrg }
1915760c2415Smrg
1916760c2415Smrg // used by both Rebindable and UnqualRef
1917760c2415Smrg private mixin template RebindableCommon(T, U, alias This)
1918760c2415Smrg if (is(T == class) || is(T == interface) || isAssociativeArray!T)
1919760c2415Smrg {
1920760c2415Smrg private union
1921760c2415Smrg {
1922760c2415Smrg T original;
1923760c2415Smrg U stripped;
1924760c2415Smrg }
1925760c2415Smrg
1926760c2415Smrg @trusted pure nothrow @nogc
1927760c2415Smrg {
1928760c2415Smrg void opAssign(T another)
1929760c2415Smrg {
1930760c2415Smrg stripped = cast(U) another;
1931760c2415Smrg }
1932760c2415Smrg
1933760c2415Smrg void opAssign(typeof(this) another)
1934760c2415Smrg {
1935760c2415Smrg stripped = another.stripped;
1936760c2415Smrg }
1937760c2415Smrg
1938760c2415Smrg static if (is(T == const U) && is(T == const shared U))
1939760c2415Smrg {
1940760c2415Smrg // safely assign immutable to const / const shared
1941760c2415Smrg void opAssign(This!(immutable U) another)
1942760c2415Smrg {
1943760c2415Smrg stripped = another.stripped;
1944760c2415Smrg }
1945760c2415Smrg }
1946760c2415Smrg
1947760c2415Smrg this(T initializer)
1948760c2415Smrg {
1949760c2415Smrg opAssign(initializer);
1950760c2415Smrg }
1951760c2415Smrg
1952760c2415Smrg @property inout(T) get() inout
1953760c2415Smrg {
1954760c2415Smrg return original;
1955760c2415Smrg }
1956760c2415Smrg }
1957760c2415Smrg
1958760c2415Smrg alias get this;
1959760c2415Smrg }
1960760c2415Smrg
1961760c2415Smrg /**
1962760c2415Smrg $(D Rebindable!(T)) is a simple, efficient wrapper that behaves just
1963760c2415Smrg like an object of type $(D T), except that you can reassign it to
1964760c2415Smrg refer to another object. For completeness, $(D Rebindable!(T)) aliases
1965760c2415Smrg itself away to $(D T) if $(D T) is a non-const object type.
1966760c2415Smrg
1967760c2415Smrg You may want to use $(D Rebindable) when you want to have mutable
1968760c2415Smrg storage referring to $(D const) objects, for example an array of
1969760c2415Smrg references that must be sorted in place. $(D Rebindable) does not
1970760c2415Smrg break the soundness of D's type system and does not incur any of the
1971760c2415Smrg risks usually associated with $(D cast).
1972760c2415Smrg
1973760c2415Smrg Params:
1974760c2415Smrg T = An object, interface, array slice type, or associative array type.
1975760c2415Smrg */
1976760c2415Smrg template Rebindable(T)
1977760c2415Smrg if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
1978760c2415Smrg {
1979760c2415Smrg static if (is(T == const U, U) || is(T == immutable U, U))
1980760c2415Smrg {
1981760c2415Smrg static if (isDynamicArray!T)
1982760c2415Smrg {
1983760c2415Smrg import std.range.primitives : ElementEncodingType;
1984760c2415Smrg alias Rebindable = const(ElementEncodingType!T)[];
1985760c2415Smrg }
1986760c2415Smrg else
1987760c2415Smrg {
1988760c2415Smrg struct Rebindable
1989760c2415Smrg {
1990760c2415Smrg mixin RebindableCommon!(T, U, Rebindable);
1991760c2415Smrg }
1992760c2415Smrg }
1993760c2415Smrg }
1994760c2415Smrg else
1995760c2415Smrg {
1996760c2415Smrg alias Rebindable = T;
1997760c2415Smrg }
1998760c2415Smrg }
1999760c2415Smrg
2000760c2415Smrg ///Regular $(D const) object references cannot be reassigned.
2001760c2415Smrg @system unittest
2002760c2415Smrg {
2003760c2415Smrg class Widget { int x; int y() const { return x; } }
2004760c2415Smrg const a = new Widget;
2005760c2415Smrg // Fine
2006760c2415Smrg a.y();
2007760c2415Smrg // error! can't modify const a
2008760c2415Smrg // a.x = 5;
2009760c2415Smrg // error! can't modify const a
2010760c2415Smrg // a = new Widget;
2011760c2415Smrg }
2012760c2415Smrg
2013760c2415Smrg /**
2014760c2415Smrg However, $(D Rebindable!(Widget)) does allow reassignment,
2015760c2415Smrg while otherwise behaving exactly like a $(D const Widget).
2016760c2415Smrg */
2017760c2415Smrg @system unittest
2018760c2415Smrg {
2019760c2415Smrg class Widget { int x; int y() const { return x; } }
2020760c2415Smrg auto a = Rebindable!(const Widget)(new Widget);
2021760c2415Smrg // Fine
2022760c2415Smrg a.y();
2023760c2415Smrg // error! can't modify const a
2024760c2415Smrg // a.x = 5;
2025760c2415Smrg // Fine
2026760c2415Smrg a = new Widget;
2027760c2415Smrg }
2028760c2415Smrg
2029760c2415Smrg @safe unittest // issue 16054
2030760c2415Smrg {
2031760c2415Smrg Rebindable!(immutable Object) r;
2032760c2415Smrg static assert(__traits(compiles, r.get()));
2033760c2415Smrg static assert(!__traits(compiles, &r.get()));
2034760c2415Smrg }
2035760c2415Smrg
2036760c2415Smrg /**
2037760c2415Smrg Convenience function for creating a $(D Rebindable) using automatic type
2038760c2415Smrg inference.
2039760c2415Smrg
2040760c2415Smrg Params:
2041760c2415Smrg obj = A reference to an object, interface, associative array, or an array slice
2042760c2415Smrg to initialize the `Rebindable` with.
2043760c2415Smrg
2044760c2415Smrg Returns:
2045760c2415Smrg A newly constructed `Rebindable` initialized with the given reference.
2046760c2415Smrg */
2047760c2415Smrg Rebindable!T rebindable(T)(T obj)
2048760c2415Smrg if (is(T == class) || is(T == interface) || isDynamicArray!T || isAssociativeArray!T)
2049760c2415Smrg {
2050760c2415Smrg typeof(return) ret;
2051760c2415Smrg ret = obj;
2052760c2415Smrg return ret;
2053760c2415Smrg }
2054760c2415Smrg
2055760c2415Smrg /**
2056760c2415Smrg This function simply returns the $(D Rebindable) object passed in. It's useful
2057760c2415Smrg in generic programming cases when a given object may be either a regular
2058760c2415Smrg $(D class) or a $(D Rebindable).
2059760c2415Smrg
2060760c2415Smrg Params:
2061760c2415Smrg obj = An instance of Rebindable!T.
2062760c2415Smrg
2063760c2415Smrg Returns:
2064760c2415Smrg `obj` without any modification.
2065760c2415Smrg */
2066760c2415Smrg Rebindable!T rebindable(T)(Rebindable!T obj)
2067760c2415Smrg {
2068760c2415Smrg return obj;
2069760c2415Smrg }
2070760c2415Smrg
2071760c2415Smrg @system unittest
2072760c2415Smrg {
2073760c2415Smrg interface CI { int foo() const; }
2074760c2415Smrg class C : CI {
2075760c2415Smrg int foo() const { return 42; }
2076760c2415Smrg @property int bar() const { return 23; }
2077760c2415Smrg }
2078760c2415Smrg Rebindable!(C) obj0;
2079760c2415Smrg static assert(is(typeof(obj0) == C));
2080760c2415Smrg
2081760c2415Smrg Rebindable!(const(C)) obj1;
2082760c2415Smrg static assert(is(typeof(obj1.get) == const(C)), typeof(obj1.get).stringof);
2083760c2415Smrg static assert(is(typeof(obj1.stripped) == C));
2084760c2415Smrg obj1 = new C;
2085760c2415Smrg assert(obj1.get !is null);
2086760c2415Smrg obj1 = new const(C);
2087760c2415Smrg assert(obj1.get !is null);
2088760c2415Smrg
2089760c2415Smrg Rebindable!(immutable(C)) obj2;
2090760c2415Smrg static assert(is(typeof(obj2.get) == immutable(C)));
2091760c2415Smrg static assert(is(typeof(obj2.stripped) == C));
2092760c2415Smrg obj2 = new immutable(C);
2093760c2415Smrg assert(obj1.get !is null);
2094760c2415Smrg
2095760c2415Smrg // test opDot
2096760c2415Smrg assert(obj2.foo() == 42);
2097760c2415Smrg assert(obj2.bar == 23);
2098760c2415Smrg
2099760c2415Smrg interface I { final int foo() const { return 42; } }
2100760c2415Smrg Rebindable!(I) obj3;
2101760c2415Smrg static assert(is(typeof(obj3) == I));
2102760c2415Smrg
2103760c2415Smrg Rebindable!(const I) obj4;
2104760c2415Smrg static assert(is(typeof(obj4.get) == const I));
2105760c2415Smrg static assert(is(typeof(obj4.stripped) == I));
2106760c2415Smrg static assert(is(typeof(obj4.foo()) == int));
2107760c2415Smrg obj4 = new class I {};
2108760c2415Smrg
2109760c2415Smrg Rebindable!(immutable C) obj5i;
2110760c2415Smrg Rebindable!(const C) obj5c;
2111760c2415Smrg obj5c = obj5c;
2112760c2415Smrg obj5c = obj5i;
2113760c2415Smrg obj5i = obj5i;
2114760c2415Smrg static assert(!__traits(compiles, obj5i = obj5c));
2115760c2415Smrg
2116760c2415Smrg // Test the convenience functions.
2117760c2415Smrg auto obj5convenience = rebindable(obj5i);
2118760c2415Smrg assert(obj5convenience is obj5i);
2119760c2415Smrg
2120760c2415Smrg auto obj6 = rebindable(new immutable(C));
2121760c2415Smrg static assert(is(typeof(obj6) == Rebindable!(immutable C)));
2122760c2415Smrg assert(obj6.foo() == 42);
2123760c2415Smrg
2124760c2415Smrg auto obj7 = rebindable(new C);
2125760c2415Smrg CI interface1 = obj7;
2126760c2415Smrg auto interfaceRebind1 = rebindable(interface1);
2127760c2415Smrg assert(interfaceRebind1.foo() == 42);
2128760c2415Smrg
2129760c2415Smrg const interface2 = interface1;
2130760c2415Smrg auto interfaceRebind2 = rebindable(interface2);
2131760c2415Smrg assert(interfaceRebind2.foo() == 42);
2132760c2415Smrg
2133760c2415Smrg auto arr = [1,2,3,4,5];
2134760c2415Smrg const arrConst = arr;
2135760c2415Smrg assert(rebindable(arr) == arr);
2136760c2415Smrg assert(rebindable(arrConst) == arr);
2137760c2415Smrg
2138760c2415Smrg // Issue 7654
2139760c2415Smrg immutable(char[]) s7654;
2140760c2415Smrg Rebindable!(typeof(s7654)) r7654 = s7654;
2141760c2415Smrg
2142760c2415Smrg foreach (T; AliasSeq!(char, wchar, char, int))
2143760c2415Smrg {
2144760c2415Smrg static assert(is(Rebindable!(immutable(T[])) == immutable(T)[]));
2145760c2415Smrg static assert(is(Rebindable!(const(T[])) == const(T)[]));
2146760c2415Smrg static assert(is(Rebindable!(T[]) == T[]));
2147760c2415Smrg }
2148760c2415Smrg
2149760c2415Smrg // Issue 12046
2150760c2415Smrg static assert(!__traits(compiles, Rebindable!(int[1])));
2151760c2415Smrg static assert(!__traits(compiles, Rebindable!(const int[1])));
2152760c2415Smrg
2153760c2415Smrg // Pull request 3341
2154760c2415Smrg Rebindable!(immutable int[int]) pr3341 = [123:345];
2155760c2415Smrg assert(pr3341[123] == 345);
2156760c2415Smrg immutable int[int] pr3341_aa = [321:543];
2157760c2415Smrg pr3341 = pr3341_aa;
2158760c2415Smrg assert(pr3341[321] == 543);
2159760c2415Smrg assert(rebindable(pr3341_aa)[321] == 543);
2160760c2415Smrg }
2161760c2415Smrg
2162760c2415Smrg /**
2163760c2415Smrg Similar to $(D Rebindable!(T)) but strips all qualifiers from the reference as
2164760c2415Smrg opposed to just constness / immutability. Primary intended use case is with
2165760c2415Smrg shared (having thread-local reference to shared class data)
2166760c2415Smrg
2167760c2415Smrg Params:
2168760c2415Smrg T = A class or interface type.
2169760c2415Smrg */
2170760c2415Smrg template UnqualRef(T)
2171760c2415Smrg if (is(T == class) || is(T == interface))
2172760c2415Smrg {
2173760c2415Smrg static if (is(T == const U, U)
2174760c2415Smrg || is(T == immutable U, U)
2175760c2415Smrg || is(T == shared U, U)
2176760c2415Smrg || is(T == const shared U, U))
2177760c2415Smrg {
2178760c2415Smrg struct UnqualRef
2179760c2415Smrg {
2180760c2415Smrg mixin RebindableCommon!(T, U, UnqualRef);
2181760c2415Smrg }
2182760c2415Smrg }
2183760c2415Smrg else
2184760c2415Smrg {
2185760c2415Smrg alias UnqualRef = T;
2186760c2415Smrg }
2187760c2415Smrg }
2188760c2415Smrg
2189760c2415Smrg ///
2190760c2415Smrg @system unittest
2191760c2415Smrg {
2192760c2415Smrg class Data {}
2193760c2415Smrg
2194760c2415Smrg static shared(Data) a;
2195760c2415Smrg static UnqualRef!(shared Data) b;
2196760c2415Smrg
2197760c2415Smrg import core.thread;
2198760c2415Smrg
2199760c2415Smrg auto thread = new core.thread.Thread({
2200760c2415Smrg a = new shared Data();
2201760c2415Smrg b = new shared Data();
2202760c2415Smrg });
2203760c2415Smrg
2204760c2415Smrg thread.start();
2205760c2415Smrg thread.join();
2206760c2415Smrg
2207760c2415Smrg assert(a !is null);
2208760c2415Smrg assert(b is null);
2209760c2415Smrg }
2210760c2415Smrg
2211760c2415Smrg @safe unittest
2212760c2415Smrg {
2213760c2415Smrg class C { }
2214760c2415Smrg alias T = UnqualRef!(const shared C);
2215760c2415Smrg static assert(is(typeof(T.stripped) == C));
2216760c2415Smrg }
2217760c2415Smrg
2218760c2415Smrg
2219760c2415Smrg
2220760c2415Smrg /**
2221760c2415Smrg Order the provided members to minimize size while preserving alignment.
2222760c2415Smrg Alignment is not always optimal for 80-bit reals, nor for structs declared
2223760c2415Smrg as align(1).
2224760c2415Smrg
2225760c2415Smrg Params:
2226760c2415Smrg E = A list of the types to be aligned, representing fields
2227760c2415Smrg of an aggregate such as a `struct` or `class`.
2228760c2415Smrg
2229760c2415Smrg names = The names of the fields that are to be aligned.
2230760c2415Smrg
2231760c2415Smrg Returns:
2232760c2415Smrg A string to be mixed in to an aggregate, such as a `struct` or `class`.
2233760c2415Smrg */
2234760c2415Smrg string alignForSize(E...)(const char[][] names...)
2235760c2415Smrg {
2236760c2415Smrg // Sort all of the members by .alignof.
2237760c2415Smrg // BUG: Alignment is not always optimal for align(1) structs
2238760c2415Smrg // or 80-bit reals or 64-bit primitives on x86.
2239760c2415Smrg // TRICK: Use the fact that .alignof is always a power of 2,
2240760c2415Smrg // and maximum 16 on extant systems. Thus, we can perform
2241760c2415Smrg // a very limited radix sort.
2242760c2415Smrg // Contains the members with .alignof = 64,32,16,8,4,2,1
2243760c2415Smrg
2244760c2415Smrg assert(E.length == names.length,
2245760c2415Smrg "alignForSize: There should be as many member names as the types");
2246760c2415Smrg
2247760c2415Smrg string[7] declaration = ["", "", "", "", "", "", ""];
2248760c2415Smrg
2249760c2415Smrg foreach (i, T; E)
2250760c2415Smrg {
2251760c2415Smrg auto a = T.alignof;
2252760c2415Smrg auto k = a >= 64? 0 : a >= 32? 1 : a >= 16? 2 : a >= 8? 3 : a >= 4? 4 : a >= 2? 5 : 6;
2253760c2415Smrg declaration[k] ~= T.stringof ~ " " ~ names[i] ~ ";\n";
2254760c2415Smrg }
2255760c2415Smrg
2256760c2415Smrg auto s = "";
2257760c2415Smrg foreach (decl; declaration)
2258760c2415Smrg s ~= decl;
2259760c2415Smrg return s;
2260760c2415Smrg }
2261760c2415Smrg
2262760c2415Smrg ///
2263760c2415Smrg @safe unittest
2264760c2415Smrg {
2265760c2415Smrg struct Banner {
2266760c2415Smrg mixin(alignForSize!(byte[6], double)(["name", "height"]));
2267760c2415Smrg }
2268760c2415Smrg }
2269760c2415Smrg
2270760c2415Smrg @safe unittest
2271760c2415Smrg {
2272760c2415Smrg enum x = alignForSize!(int[], char[3], short, double[5])("x", "y","z", "w");
2273760c2415Smrg struct Foo { int x; }
2274760c2415Smrg enum y = alignForSize!(ubyte, Foo, cdouble)("x", "y", "z");
2275760c2415Smrg
2276760c2415Smrg enum passNormalX = x == "double[5] w;\nint[] x;\nshort z;\nchar[3] y;\n";
2277760c2415Smrg enum passNormalY = y == "cdouble z;\nFoo y;\nubyte x;\n";
2278760c2415Smrg
2279760c2415Smrg enum passAbnormalX = x == "int[] x;\ndouble[5] w;\nshort z;\nchar[3] y;\n";
2280760c2415Smrg enum passAbnormalY = y == "Foo y;\ncdouble z;\nubyte x;\n";
2281760c2415Smrg // ^ blame http://d.puremagic.com/issues/show_bug.cgi?id=231
2282760c2415Smrg
2283760c2415Smrg static assert(passNormalX || passAbnormalX && double.alignof <= (int[]).alignof);
2284760c2415Smrg static assert(passNormalY || passAbnormalY && double.alignof <= int.alignof);
2285760c2415Smrg }
2286760c2415Smrg
2287760c2415Smrg // Issue 12914
2288760c2415Smrg @safe unittest
2289760c2415Smrg {
2290760c2415Smrg immutable string[] fieldNames = ["x", "y"];
2291760c2415Smrg struct S
2292760c2415Smrg {
2293760c2415Smrg mixin(alignForSize!(byte, int)(fieldNames));
2294760c2415Smrg }
2295760c2415Smrg }
2296760c2415Smrg
2297760c2415Smrg /**
2298760c2415Smrg Defines a value paired with a distinctive "null" state that denotes
2299760c2415Smrg the absence of a value. If default constructed, a $(D
2300760c2415Smrg Nullable!T) object starts in the null state. Assigning it renders it
2301760c2415Smrg non-null. Calling $(D nullify) can nullify it again.
2302760c2415Smrg
2303760c2415Smrg Practically $(D Nullable!T) stores a $(D T) and a $(D bool).
2304760c2415Smrg */
2305760c2415Smrg struct Nullable(T)
2306760c2415Smrg {
2307760c2415Smrg private T _value;
2308760c2415Smrg private bool _isNull = true;
2309760c2415Smrg
2310760c2415Smrg /**
2311760c2415Smrg Constructor initializing $(D this) with $(D value).
2312760c2415Smrg
2313760c2415Smrg Params:
2314760c2415Smrg value = The value to initialize this `Nullable` with.
2315760c2415Smrg */
2316760c2415Smrg this(inout T value) inout
2317760c2415Smrg {
2318760c2415Smrg _value = value;
2319760c2415Smrg _isNull = false;
2320760c2415Smrg }
2321760c2415Smrg
2322760c2415Smrg /**
2323760c2415Smrg If they are both null, then they are equal. If one is null and the other
2324760c2415Smrg is not, then they are not equal. If they are both non-null, then they are
2325760c2415Smrg equal if their values are equal.
2326760c2415Smrg */
2327760c2415Smrg bool opEquals()(auto ref const(typeof(this)) rhs) const
2328760c2415Smrg {
2329760c2415Smrg if (_isNull)
2330760c2415Smrg return rhs._isNull;
2331760c2415Smrg if (rhs._isNull)
2332760c2415Smrg return false;
2333760c2415Smrg return _value == rhs._value;
2334760c2415Smrg }
2335760c2415Smrg
2336760c2415Smrg /// Ditto
2337760c2415Smrg bool opEquals(U)(auto ref const(U) rhs) const
2338760c2415Smrg if (is(typeof(this.get == rhs)))
2339760c2415Smrg {
2340760c2415Smrg return _isNull ? false : rhs == _value;
2341760c2415Smrg }
2342760c2415Smrg
2343760c2415Smrg ///
2344760c2415Smrg @safe unittest
2345760c2415Smrg {
2346760c2415Smrg Nullable!int empty;
2347760c2415Smrg Nullable!int a = 42;
2348760c2415Smrg Nullable!int b = 42;
2349760c2415Smrg Nullable!int c = 27;
2350760c2415Smrg
2351760c2415Smrg assert(empty == empty);
2352760c2415Smrg assert(empty == Nullable!int.init);
2353760c2415Smrg assert(empty != a);
2354760c2415Smrg assert(empty != b);
2355760c2415Smrg assert(empty != c);
2356760c2415Smrg
2357760c2415Smrg assert(a == b);
2358760c2415Smrg assert(a != c);
2359760c2415Smrg
2360760c2415Smrg assert(empty != 42);
2361760c2415Smrg assert(a == 42);
2362760c2415Smrg assert(c != 42);
2363760c2415Smrg }
2364760c2415Smrg
2365760c2415Smrg @safe unittest
2366760c2415Smrg {
2367760c2415Smrg // Test constness
2368760c2415Smrg immutable Nullable!int a = 42;
2369760c2415Smrg Nullable!int b = 42;
2370760c2415Smrg immutable Nullable!int c = 29;
2371760c2415Smrg Nullable!int d = 29;
2372760c2415Smrg immutable e = 42;
2373760c2415Smrg int f = 29;
2374760c2415Smrg assert(a == a);
2375760c2415Smrg assert(a == b);
2376760c2415Smrg assert(a != c);
2377760c2415Smrg assert(a != d);
2378760c2415Smrg assert(a == e);
2379760c2415Smrg assert(a != f);
2380760c2415Smrg
2381760c2415Smrg // Test rvalue
2382760c2415Smrg assert(a == const Nullable!int(42));
2383760c2415Smrg assert(a != Nullable!int(29));
2384760c2415Smrg }
2385760c2415Smrg
2386760c2415Smrg // Issue 17482
2387760c2415Smrg @system unittest
2388760c2415Smrg {
2389760c2415Smrg import std.variant : Variant;
2390760c2415Smrg Nullable!Variant a = Variant(12);
2391760c2415Smrg assert(a == 12);
2392760c2415Smrg Nullable!Variant e;
2393760c2415Smrg assert(e != 12);
2394760c2415Smrg }
2395760c2415Smrg
2396760c2415Smrg template toString()
2397760c2415Smrg {
2398760c2415Smrg import std.format : FormatSpec, formatValue;
2399760c2415Smrg // Needs to be a template because of DMD @@BUG@@ 13737.
2400760c2415Smrg void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
2401760c2415Smrg {
2402760c2415Smrg if (isNull)
2403760c2415Smrg {
2404760c2415Smrg sink.formatValue("Nullable.null", fmt);
2405760c2415Smrg }
2406760c2415Smrg else
2407760c2415Smrg {
2408760c2415Smrg sink.formatValue(_value, fmt);
2409760c2415Smrg }
2410760c2415Smrg }
2411760c2415Smrg
2412760c2415Smrg // Issue 14940
2413760c2415Smrg void toString()(scope void delegate(const(char)[]) @safe sink, FormatSpec!char fmt)
2414760c2415Smrg {
2415760c2415Smrg if (isNull)
2416760c2415Smrg {
2417760c2415Smrg sink.formatValue("Nullable.null", fmt);
2418760c2415Smrg }
2419760c2415Smrg else
2420760c2415Smrg {
2421760c2415Smrg sink.formatValue(_value, fmt);
2422760c2415Smrg }
2423760c2415Smrg }
2424760c2415Smrg }
2425760c2415Smrg
2426760c2415Smrg /**
2427760c2415Smrg Check if `this` is in the null state.
2428760c2415Smrg
2429760c2415Smrg Returns:
2430760c2415Smrg true $(B iff) `this` is in the null state, otherwise false.
2431760c2415Smrg */
2432760c2415Smrg @property bool isNull() const @safe pure nothrow
2433760c2415Smrg {
2434760c2415Smrg return _isNull;
2435760c2415Smrg }
2436760c2415Smrg
2437760c2415Smrg ///
2438760c2415Smrg @system unittest
2439760c2415Smrg {
2440760c2415Smrg Nullable!int ni;
2441760c2415Smrg assert(ni.isNull);
2442760c2415Smrg
2443760c2415Smrg ni = 0;
2444760c2415Smrg assert(!ni.isNull);
2445760c2415Smrg }
2446760c2415Smrg
2447760c2415Smrg // Issue 14940
2448760c2415Smrg @safe unittest
2449760c2415Smrg {
2450760c2415Smrg import std.array : appender;
2451760c2415Smrg import std.format : formattedWrite;
2452760c2415Smrg
2453760c2415Smrg auto app = appender!string();
2454760c2415Smrg Nullable!int a = 1;
2455760c2415Smrg formattedWrite(app, "%s", a);
2456760c2415Smrg assert(app.data == "1");
2457760c2415Smrg }
2458760c2415Smrg
2459760c2415Smrg /**
2460760c2415Smrg Forces $(D this) to the null state.
2461760c2415Smrg */
2462760c2415Smrg void nullify()()
2463760c2415Smrg {
2464760c2415Smrg .destroy(_value);
2465760c2415Smrg _isNull = true;
2466760c2415Smrg }
2467760c2415Smrg
2468760c2415Smrg ///
2469760c2415Smrg @safe unittest
2470760c2415Smrg {
2471760c2415Smrg Nullable!int ni = 0;
2472760c2415Smrg assert(!ni.isNull);
2473760c2415Smrg
2474760c2415Smrg ni.nullify();
2475760c2415Smrg assert(ni.isNull);
2476760c2415Smrg }
2477760c2415Smrg
2478760c2415Smrg /**
2479760c2415Smrg Assigns $(D value) to the internally-held state. If the assignment
2480760c2415Smrg succeeds, $(D this) becomes non-null.
2481760c2415Smrg
2482760c2415Smrg Params:
2483760c2415Smrg value = A value of type `T` to assign to this `Nullable`.
2484760c2415Smrg */
2485760c2415Smrg void opAssign()(T value)
2486760c2415Smrg {
2487760c2415Smrg _value = value;
2488760c2415Smrg _isNull = false;
2489760c2415Smrg }
2490760c2415Smrg
2491760c2415Smrg /**
2492760c2415Smrg If this `Nullable` wraps a type that already has a null value
2493760c2415Smrg (such as a pointer), then assigning the null value to this
2494760c2415Smrg `Nullable` is no different than assigning any other value of
2495760c2415Smrg type `T`, and the resulting code will look very strange. It
2496760c2415Smrg is strongly recommended that this be avoided by instead using
2497760c2415Smrg the version of `Nullable` that takes an additional `nullValue`
2498760c2415Smrg template argument.
2499760c2415Smrg */
2500760c2415Smrg @safe unittest
2501760c2415Smrg {
2502760c2415Smrg //Passes
2503760c2415Smrg Nullable!(int*) npi;
2504760c2415Smrg assert(npi.isNull);
2505760c2415Smrg
2506760c2415Smrg //Passes?!
2507760c2415Smrg npi = null;
2508760c2415Smrg assert(!npi.isNull);
2509760c2415Smrg }
2510760c2415Smrg
2511760c2415Smrg /**
2512760c2415Smrg Gets the value. $(D this) must not be in the null state.
2513760c2415Smrg This function is also called for the implicit conversion to $(D T).
2514760c2415Smrg
2515760c2415Smrg Returns:
2516760c2415Smrg The value held internally by this `Nullable`.
2517760c2415Smrg */
2518760c2415Smrg @property ref inout(T) get() inout @safe pure nothrow
2519760c2415Smrg {
2520760c2415Smrg enum message = "Called `get' on null Nullable!" ~ T.stringof ~ ".";
2521760c2415Smrg assert(!isNull, message);
2522760c2415Smrg return _value;
2523760c2415Smrg }
2524760c2415Smrg
2525760c2415Smrg ///
2526760c2415Smrg @system unittest
2527760c2415Smrg {
2528760c2415Smrg import core.exception : AssertError;
2529760c2415Smrg import std.exception : assertThrown, assertNotThrown;
2530760c2415Smrg
2531760c2415Smrg Nullable!int ni;
2532760c2415Smrg int i = 42;
2533760c2415Smrg //`get` is implicitly called. Will throw
2534760c2415Smrg //an AssertError in non-release mode
2535760c2415Smrg assertThrown!AssertError(i = ni);
2536760c2415Smrg assert(i == 42);
2537760c2415Smrg
2538760c2415Smrg ni = 5;
2539760c2415Smrg assertNotThrown!AssertError(i = ni);
2540760c2415Smrg assert(i == 5);
2541760c2415Smrg }
2542760c2415Smrg
2543760c2415Smrg /**
2544760c2415Smrg Implicitly converts to $(D T).
2545760c2415Smrg $(D this) must not be in the null state.
2546760c2415Smrg */
2547760c2415Smrg alias get this;
2548760c2415Smrg }
2549760c2415Smrg
2550760c2415Smrg /// ditto
2551760c2415Smrg auto nullable(T)(T t)
2552760c2415Smrg {
2553760c2415Smrg return Nullable!T(t);
2554760c2415Smrg }
2555760c2415Smrg
2556760c2415Smrg ///
2557760c2415Smrg @safe unittest
2558760c2415Smrg {
2559760c2415Smrg struct CustomerRecord
2560760c2415Smrg {
2561760c2415Smrg string name;
2562760c2415Smrg string address;
2563760c2415Smrg int customerNum;
2564760c2415Smrg }
2565760c2415Smrg
2566760c2415Smrg Nullable!CustomerRecord getByName(string name)
2567760c2415Smrg {
2568760c2415Smrg //A bunch of hairy stuff
2569760c2415Smrg
2570760c2415Smrg return Nullable!CustomerRecord.init;
2571760c2415Smrg }
2572760c2415Smrg
2573760c2415Smrg auto queryResult = getByName("Doe, John");
2574760c2415Smrg if (!queryResult.isNull)
2575760c2415Smrg {
2576760c2415Smrg //Process Mr. Doe's customer record
2577760c2415Smrg auto address = queryResult.address;
2578760c2415Smrg auto customerNum = queryResult.customerNum;
2579760c2415Smrg
2580760c2415Smrg //Do some things with this customer's info
2581760c2415Smrg }
2582760c2415Smrg else
2583760c2415Smrg {
2584760c2415Smrg //Add the customer to the database
2585760c2415Smrg }
2586760c2415Smrg }
2587760c2415Smrg
2588760c2415Smrg ///
2589760c2415Smrg @system unittest
2590760c2415Smrg {
2591760c2415Smrg import std.exception : assertThrown;
2592760c2415Smrg
2593760c2415Smrg auto a = 42.nullable;
2594760c2415Smrg assert(!a.isNull);
2595760c2415Smrg assert(a.get == 42);
2596760c2415Smrg
2597760c2415Smrg a.nullify();
2598760c2415Smrg assert(a.isNull);
2599760c2415Smrg assertThrown!Throwable(a.get);
2600760c2415Smrg }
2601760c2415Smrg
2602760c2415Smrg @system unittest
2603760c2415Smrg {
2604760c2415Smrg import std.exception : assertThrown;
2605760c2415Smrg
2606760c2415Smrg Nullable!int a;
2607760c2415Smrg assert(a.isNull);
2608760c2415Smrg assertThrown!Throwable(a.get);
2609760c2415Smrg a = 5;
2610760c2415Smrg assert(!a.isNull);
2611760c2415Smrg assert(a == 5);
2612760c2415Smrg assert(a != 3);
2613760c2415Smrg assert(a.get != 3);
2614760c2415Smrg a.nullify();
2615760c2415Smrg assert(a.isNull);
2616760c2415Smrg a = 3;
2617760c2415Smrg assert(a == 3);
2618760c2415Smrg a *= 6;
2619760c2415Smrg assert(a == 18);
2620760c2415Smrg a = a;
2621760c2415Smrg assert(a == 18);
2622760c2415Smrg a.nullify();
2623760c2415Smrg assertThrown!Throwable(a += 2);
2624760c2415Smrg }
2625760c2415Smrg @safe unittest
2626760c2415Smrg {
2627760c2415Smrg auto k = Nullable!int(74);
2628760c2415Smrg assert(k == 74);
2629760c2415Smrg k.nullify();
2630760c2415Smrg assert(k.isNull);
2631760c2415Smrg }
2632760c2415Smrg @safe unittest
2633760c2415Smrg {
2634760c2415Smrg static int f(in Nullable!int x) {
2635760c2415Smrg return x.isNull ? 42 : x.get;
2636760c2415Smrg }
2637760c2415Smrg Nullable!int a;
2638760c2415Smrg assert(f(a) == 42);
2639760c2415Smrg a = 8;
2640760c2415Smrg assert(f(a) == 8);
2641760c2415Smrg a.nullify();
2642760c2415Smrg assert(f(a) == 42);
2643760c2415Smrg }
2644760c2415Smrg @system unittest
2645760c2415Smrg {
2646760c2415Smrg import std.exception : assertThrown;
2647760c2415Smrg
2648760c2415Smrg static struct S { int x; }
2649760c2415Smrg Nullable!S s;
2650760c2415Smrg assert(s.isNull);
2651760c2415Smrg s = S(6);
2652760c2415Smrg assert(s == S(6));
2653760c2415Smrg assert(s != S(0));
2654760c2415Smrg assert(s.get != S(0));
2655760c2415Smrg s.x = 9190;
2656760c2415Smrg assert(s.x == 9190);
2657760c2415Smrg s.nullify();
2658760c2415Smrg assertThrown!Throwable(s.x = 9441);
2659760c2415Smrg }
2660760c2415Smrg @safe unittest
2661760c2415Smrg {
2662760c2415Smrg // Ensure Nullable can be used in pure/nothrow/@safe environment.
2663760c2415Smrg function() @safe pure nothrow
2664760c2415Smrg {
2665760c2415Smrg Nullable!int n;
2666760c2415Smrg assert(n.isNull);
2667760c2415Smrg n = 4;
2668760c2415Smrg assert(!n.isNull);
2669760c2415Smrg assert(n == 4);
2670760c2415Smrg n.nullify();
2671760c2415Smrg assert(n.isNull);
2672760c2415Smrg }();
2673760c2415Smrg }
2674760c2415Smrg @system unittest
2675760c2415Smrg {
2676760c2415Smrg // Ensure Nullable can be used when the value is not pure/nothrow/@safe
2677760c2415Smrg static struct S
2678760c2415Smrg {
2679760c2415Smrg int x;
2680760c2415Smrg this(this) @system {}
2681760c2415Smrg }
2682760c2415Smrg
2683760c2415Smrg Nullable!S s;
2684760c2415Smrg assert(s.isNull);
2685760c2415Smrg s = S(5);
2686760c2415Smrg assert(!s.isNull);
2687760c2415Smrg assert(s.x == 5);
2688760c2415Smrg s.nullify();
2689760c2415Smrg assert(s.isNull);
2690760c2415Smrg }
2691760c2415Smrg @safe unittest
2692760c2415Smrg {
2693760c2415Smrg // Bugzilla 9404
2694760c2415Smrg alias N = Nullable!int;
2695760c2415Smrg
2696760c2415Smrg void foo(N a)
2697760c2415Smrg {
2698760c2415Smrg N b;
2699760c2415Smrg b = a; // `N b = a;` works fine
2700760c2415Smrg }
2701760c2415Smrg N n;
2702760c2415Smrg foo(n);
2703760c2415Smrg }
2704760c2415Smrg @safe unittest
2705760c2415Smrg {
2706760c2415Smrg //Check nullable immutable is constructable
2707760c2415Smrg {
2708760c2415Smrg auto a1 = Nullable!(immutable int)();
2709760c2415Smrg auto a2 = Nullable!(immutable int)(1);
2710760c2415Smrg auto i = a2.get;
2711760c2415Smrg }
2712760c2415Smrg //Check immutable nullable is constructable
2713760c2415Smrg {
2714760c2415Smrg auto a1 = immutable (Nullable!int)();
2715760c2415Smrg auto a2 = immutable (Nullable!int)(1);
2716760c2415Smrg auto i = a2.get;
2717760c2415Smrg }
2718760c2415Smrg }
2719760c2415Smrg @safe unittest
2720760c2415Smrg {
2721760c2415Smrg alias NInt = Nullable!int;
2722760c2415Smrg
2723760c2415Smrg //Construct tests
2724760c2415Smrg {
2725760c2415Smrg //from other Nullable null
2726760c2415Smrg NInt a1;
2727760c2415Smrg NInt b1 = a1;
2728760c2415Smrg assert(b1.isNull);
2729760c2415Smrg
2730760c2415Smrg //from other Nullable non-null
2731760c2415Smrg NInt a2 = NInt(1);
2732760c2415Smrg NInt b2 = a2;
2733760c2415Smrg assert(b2 == 1);
2734760c2415Smrg
2735760c2415Smrg //Construct from similar nullable
2736760c2415Smrg auto a3 = immutable(NInt)();
2737760c2415Smrg NInt b3 = a3;
2738760c2415Smrg assert(b3.isNull);
2739760c2415Smrg }
2740760c2415Smrg
2741760c2415Smrg //Assign tests
2742760c2415Smrg {
2743760c2415Smrg //from other Nullable null
2744760c2415Smrg NInt a1;
2745760c2415Smrg NInt b1;
2746760c2415Smrg b1 = a1;
2747760c2415Smrg assert(b1.isNull);
2748760c2415Smrg
2749760c2415Smrg //from other Nullable non-null
2750760c2415Smrg NInt a2 = NInt(1);
2751760c2415Smrg NInt b2;
2752760c2415Smrg b2 = a2;
2753760c2415Smrg assert(b2 == 1);
2754760c2415Smrg
2755760c2415Smrg //Construct from similar nullable
2756760c2415Smrg auto a3 = immutable(NInt)();
2757760c2415Smrg NInt b3 = a3;
2758760c2415Smrg b3 = a3;
2759760c2415Smrg assert(b3.isNull);
2760760c2415Smrg }
2761760c2415Smrg }
2762760c2415Smrg @safe unittest
2763760c2415Smrg {
2764760c2415Smrg //Check nullable is nicelly embedable in a struct
2765760c2415Smrg static struct S1
2766760c2415Smrg {
2767760c2415Smrg Nullable!int ni;
2768760c2415Smrg }
2769760c2415Smrg static struct S2 //inspired from 9404
2770760c2415Smrg {
2771760c2415Smrg Nullable!int ni;
2772760c2415Smrg this(S2 other)
2773760c2415Smrg {
2774760c2415Smrg ni = other.ni;
2775760c2415Smrg }
2776760c2415Smrg void opAssign(S2 other)
2777760c2415Smrg {
2778760c2415Smrg ni = other.ni;
2779760c2415Smrg }
2780760c2415Smrg }
2781760c2415Smrg foreach (S; AliasSeq!(S1, S2))
2782760c2415Smrg {
2783760c2415Smrg S a;
2784760c2415Smrg S b = a;
2785760c2415Smrg S c;
2786760c2415Smrg c = a;
2787760c2415Smrg }
2788760c2415Smrg }
2789760c2415Smrg @system unittest
2790760c2415Smrg {
2791760c2415Smrg // Bugzilla 10268
2792760c2415Smrg import std.json;
2793760c2415Smrg JSONValue value = null;
2794760c2415Smrg auto na = Nullable!JSONValue(value);
2795760c2415Smrg
2796760c2415Smrg struct S1 { int val; }
2797760c2415Smrg struct S2 { int* val; }
2798760c2415Smrg struct S3 { immutable int* val; }
2799760c2415Smrg
2800760c2415Smrg {
2801760c2415Smrg auto sm = S1(1);
2802760c2415Smrg immutable si = immutable S1(1);
2803760c2415Smrg auto x1 = Nullable!S1(sm);
2804760c2415Smrg auto x2 = immutable Nullable!S1(sm);
2805760c2415Smrg auto x3 = Nullable!S1(si);
2806760c2415Smrg auto x4 = immutable Nullable!S1(si);
2807760c2415Smrg assert(x1.val == 1);
2808760c2415Smrg assert(x2.val == 1);
2809760c2415Smrg assert(x3.val == 1);
2810760c2415Smrg assert(x4.val == 1);
2811760c2415Smrg }
2812760c2415Smrg
2813760c2415Smrg auto nm = 10;
2814760c2415Smrg immutable ni = 10;
2815760c2415Smrg
2816760c2415Smrg {
2817760c2415Smrg auto sm = S2(&nm);
2818760c2415Smrg immutable si = immutable S2(&ni);
2819760c2415Smrg auto x1 = Nullable!S2(sm);
2820760c2415Smrg static assert(!__traits(compiles, { auto x2 = immutable Nullable!S2(sm); }));
2821760c2415Smrg static assert(!__traits(compiles, { auto x3 = Nullable!S2(si); }));
2822760c2415Smrg auto x4 = immutable Nullable!S2(si);
2823760c2415Smrg assert(*x1.val == 10);
2824760c2415Smrg assert(*x4.val == 10);
2825760c2415Smrg }
2826760c2415Smrg
2827760c2415Smrg {
2828760c2415Smrg auto sm = S3(&ni);
2829760c2415Smrg immutable si = immutable S3(&ni);
2830760c2415Smrg auto x1 = Nullable!S3(sm);
2831760c2415Smrg auto x2 = immutable Nullable!S3(sm);
2832760c2415Smrg auto x3 = Nullable!S3(si);
2833760c2415Smrg auto x4 = immutable Nullable!S3(si);
2834760c2415Smrg assert(*x1.val == 10);
2835760c2415Smrg assert(*x2.val == 10);
2836760c2415Smrg assert(*x3.val == 10);
2837760c2415Smrg assert(*x4.val == 10);
2838760c2415Smrg }
2839760c2415Smrg }
2840760c2415Smrg @safe unittest
2841760c2415Smrg {
2842760c2415Smrg // Bugzila 10357
2843760c2415Smrg import std.datetime;
2844760c2415Smrg Nullable!SysTime time = SysTime(0);
2845760c2415Smrg }
2846760c2415Smrg @system unittest
2847760c2415Smrg {
2848760c2415Smrg import std.conv : to;
2849760c2415Smrg import std.array;
2850760c2415Smrg
2851760c2415Smrg // Bugzilla 10915
2852760c2415Smrg Appender!string buffer;
2853760c2415Smrg
2854760c2415Smrg Nullable!int ni;
2855760c2415Smrg assert(ni.to!string() == "Nullable.null");
2856760c2415Smrg
2857760c2415Smrg struct Test { string s; }
2858760c2415Smrg alias NullableTest = Nullable!Test;
2859760c2415Smrg
2860760c2415Smrg NullableTest nt = Test("test");
2861760c2415Smrg assert(nt.to!string() == `Test("test")`);
2862760c2415Smrg
2863760c2415Smrg NullableTest ntn = Test("null");
2864760c2415Smrg assert(ntn.to!string() == `Test("null")`);
2865760c2415Smrg
2866760c2415Smrg class TestToString
2867760c2415Smrg {
2868760c2415Smrg double d;
2869760c2415Smrg
2870760c2415Smrg this (double d)
2871760c2415Smrg {
2872760c2415Smrg this.d = d;
2873760c2415Smrg }
2874760c2415Smrg
2875760c2415Smrg override string toString()
2876760c2415Smrg {
2877760c2415Smrg return d.to!string();
2878760c2415Smrg }
2879760c2415Smrg }
2880760c2415Smrg Nullable!TestToString ntts = new TestToString(2.5);
2881760c2415Smrg assert(ntts.to!string() == "2.5");
2882760c2415Smrg }
2883760c2415Smrg
2884760c2415Smrg /**
2885760c2415Smrg Just like $(D Nullable!T), except that the null state is defined as a
2886760c2415Smrg particular value. For example, $(D Nullable!(uint, uint.max)) is an
2887760c2415Smrg $(D uint) that sets aside the value $(D uint.max) to denote a null
2888760c2415Smrg state. $(D Nullable!(T, nullValue)) is more storage-efficient than $(D
2889760c2415Smrg Nullable!T) because it does not need to store an extra $(D bool).
2890760c2415Smrg
2891760c2415Smrg Params:
2892760c2415Smrg T = The wrapped type for which Nullable provides a null value.
2893760c2415Smrg
2894760c2415Smrg nullValue = The null value which denotes the null state of this
2895760c2415Smrg `Nullable`. Must be of type `T`.
2896760c2415Smrg */
2897760c2415Smrg struct Nullable(T, T nullValue)
2898760c2415Smrg {
2899760c2415Smrg private T _value = nullValue;
2900760c2415Smrg
2901760c2415Smrg /**
2902760c2415Smrg Constructor initializing $(D this) with $(D value).
2903760c2415Smrg
2904760c2415Smrg Params:
2905760c2415Smrg value = The value to initialize this `Nullable` with.
2906760c2415Smrg */
2907760c2415Smrg this(T value)
2908760c2415Smrg {
2909760c2415Smrg _value = value;
2910760c2415Smrg }
2911760c2415Smrg
2912760c2415Smrg template toString()
2913760c2415Smrg {
2914760c2415Smrg import std.format : FormatSpec, formatValue;
2915760c2415Smrg // Needs to be a template because of DMD @@BUG@@ 13737.
2916760c2415Smrg void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
2917760c2415Smrg {
2918760c2415Smrg if (isNull)
2919760c2415Smrg {
2920760c2415Smrg sink.formatValue("Nullable.null", fmt);
2921760c2415Smrg }
2922760c2415Smrg else
2923760c2415Smrg {
2924760c2415Smrg sink.formatValue(_value, fmt);
2925760c2415Smrg }
2926760c2415Smrg }
2927760c2415Smrg }
2928760c2415Smrg
2929760c2415Smrg /**
2930760c2415Smrg Check if `this` is in the null state.
2931760c2415Smrg
2932760c2415Smrg Returns:
2933760c2415Smrg true $(B iff) `this` is in the null state, otherwise false.
2934760c2415Smrg */
2935760c2415Smrg @property bool isNull() const
2936760c2415Smrg {
2937760c2415Smrg //Need to use 'is' if T is a nullable type and
2938760c2415Smrg //nullValue is null, or it's a compiler error
2939760c2415Smrg static if (is(CommonType!(T, typeof(null)) == T) && nullValue is null)
2940760c2415Smrg {
2941760c2415Smrg return _value is nullValue;
2942760c2415Smrg }
2943760c2415Smrg //Need to use 'is' if T is a float type
2944760c2415Smrg //because NaN != NaN
2945760c2415Smrg else static if (isFloatingPoint!T)
2946760c2415Smrg {
2947760c2415Smrg return _value is nullValue;
2948760c2415Smrg }
2949760c2415Smrg else
2950760c2415Smrg {
2951760c2415Smrg return _value == nullValue;
2952760c2415Smrg }
2953760c2415Smrg }
2954760c2415Smrg
2955760c2415Smrg ///
2956760c2415Smrg @system unittest
2957760c2415Smrg {
2958760c2415Smrg Nullable!(int, -1) ni;
2959760c2415Smrg //Initialized to "null" state
2960760c2415Smrg assert(ni.isNull);
2961760c2415Smrg
2962760c2415Smrg ni = 0;
2963760c2415Smrg assert(!ni.isNull);
2964760c2415Smrg }
2965760c2415Smrg
2966760c2415Smrg // https://issues.dlang.org/show_bug.cgi?id=11135
2967760c2415Smrg // disable test until https://issues.dlang.org/show_bug.cgi?id=15316 gets fixed
2968760c2415Smrg version (none) @system unittest
2969760c2415Smrg {
2970760c2415Smrg foreach (T; AliasSeq!(float, double, real))
2971760c2415Smrg {
2972760c2415Smrg Nullable!(T, T.init) nf;
2973760c2415Smrg //Initialized to "null" state
2974760c2415Smrg assert(nf.isNull);
2975760c2415Smrg assert(nf is typeof(nf).init);
2976760c2415Smrg
2977760c2415Smrg nf = 0;
2978760c2415Smrg assert(!nf.isNull);
2979760c2415Smrg
2980760c2415Smrg nf.nullify();
2981760c2415Smrg assert(nf.isNull);
2982760c2415Smrg }
2983760c2415Smrg }
2984760c2415Smrg
2985760c2415Smrg /**
2986760c2415Smrg Forces $(D this) to the null state.
2987760c2415Smrg */
2988760c2415Smrg void nullify()()
2989760c2415Smrg {
2990760c2415Smrg _value = nullValue;
2991760c2415Smrg }
2992760c2415Smrg
2993760c2415Smrg ///
2994760c2415Smrg @system unittest
2995760c2415Smrg {
2996760c2415Smrg Nullable!(int, -1) ni = 0;
2997760c2415Smrg assert(!ni.isNull);
2998760c2415Smrg
2999760c2415Smrg ni = -1;
3000760c2415Smrg assert(ni.isNull);
3001760c2415Smrg }
3002760c2415Smrg
3003760c2415Smrg /**
3004760c2415Smrg Assigns $(D value) to the internally-held state. If the assignment
3005760c2415Smrg succeeds, $(D this) becomes non-null. No null checks are made. Note
3006760c2415Smrg that the assignment may leave $(D this) in the null state.
3007760c2415Smrg
3008760c2415Smrg Params:
3009760c2415Smrg value = A value of type `T` to assign to this `Nullable`.
3010760c2415Smrg If it is `nullvalue`, then the internal state of
3011760c2415Smrg this `Nullable` will be set to null.
3012760c2415Smrg */
3013760c2415Smrg void opAssign()(T value)
3014760c2415Smrg {
3015760c2415Smrg _value = value;
3016760c2415Smrg }
3017760c2415Smrg
3018760c2415Smrg /**
3019760c2415Smrg If this `Nullable` wraps a type that already has a null value
3020760c2415Smrg (such as a pointer), and that null value is not given for
3021760c2415Smrg `nullValue`, then assigning the null value to this `Nullable`
3022760c2415Smrg is no different than assigning any other value of type `T`,
3023760c2415Smrg and the resulting code will look very strange. It is strongly
3024760c2415Smrg recommended that this be avoided by using `T`'s "built in"
3025760c2415Smrg null value for `nullValue`.
3026760c2415Smrg */
3027760c2415Smrg @system unittest
3028760c2415Smrg {
3029760c2415Smrg //Passes
3030760c2415Smrg enum nullVal = cast(int*) 0xCAFEBABE;
3031760c2415Smrg Nullable!(int*, nullVal) npi;
3032760c2415Smrg assert(npi.isNull);
3033760c2415Smrg
3034760c2415Smrg //Passes?!
3035760c2415Smrg npi = null;
3036760c2415Smrg assert(!npi.isNull);
3037760c2415Smrg }
3038760c2415Smrg
3039760c2415Smrg /**
3040760c2415Smrg Gets the value. $(D this) must not be in the null state.
3041760c2415Smrg This function is also called for the implicit conversion to $(D T).
3042760c2415Smrg
3043760c2415Smrg Returns:
3044760c2415Smrg The value held internally by this `Nullable`.
3045760c2415Smrg */
3046760c2415Smrg @property ref inout(T) get() inout
3047760c2415Smrg {
3048760c2415Smrg //@@@6169@@@: We avoid any call that might evaluate nullValue's %s,
3049760c2415Smrg //Because it might messup get's purity and safety inference.
3050760c2415Smrg enum message = "Called `get' on null Nullable!(" ~ T.stringof ~ ",nullValue).";
3051760c2415Smrg assert(!isNull, message);
3052760c2415Smrg return _value;
3053760c2415Smrg }
3054760c2415Smrg
3055760c2415Smrg ///
3056760c2415Smrg @system unittest
3057760c2415Smrg {
3058760c2415Smrg import std.exception : assertThrown, assertNotThrown;
3059760c2415Smrg
3060760c2415Smrg Nullable!(int, -1) ni;
3061760c2415Smrg //`get` is implicitly called. Will throw
3062760c2415Smrg //an error in non-release mode
3063760c2415Smrg assertThrown!Throwable(ni == 0);
3064760c2415Smrg
3065760c2415Smrg ni = 0;
3066760c2415Smrg assertNotThrown!Throwable(ni == 0);
3067760c2415Smrg }
3068760c2415Smrg
3069760c2415Smrg /**
3070760c2415Smrg Implicitly converts to $(D T).
3071760c2415Smrg $(D this) must not be in the null state.
3072760c2415Smrg */
3073760c2415Smrg alias get this;
3074760c2415Smrg }
3075760c2415Smrg
3076760c2415Smrg /// ditto
3077760c2415Smrg auto nullable(alias nullValue, T)(T t)
3078760c2415Smrg if (is (typeof(nullValue) == T))
3079760c2415Smrg {
3080760c2415Smrg return Nullable!(T, nullValue)(t);
3081760c2415Smrg }
3082760c2415Smrg
3083760c2415Smrg ///
3084760c2415Smrg @safe unittest
3085760c2415Smrg {
3086760c2415Smrg Nullable!(size_t, size_t.max) indexOf(string[] haystack, string needle)
3087760c2415Smrg {
3088760c2415Smrg //Find the needle, returning -1 if not found
3089760c2415Smrg
3090760c2415Smrg return Nullable!(size_t, size_t.max).init;
3091760c2415Smrg }
3092760c2415Smrg
3093760c2415Smrg void sendLunchInvite(string name)
3094760c2415Smrg {
3095760c2415Smrg }
3096760c2415Smrg
3097760c2415Smrg //It's safer than C...
3098760c2415Smrg auto coworkers = ["Jane", "Jim", "Marry", "Fred"];
3099760c2415Smrg auto pos = indexOf(coworkers, "Bob");
3100760c2415Smrg if (!pos.isNull)
3101760c2415Smrg {
3102760c2415Smrg //Send Bob an invitation to lunch
3103760c2415Smrg sendLunchInvite(coworkers[pos]);
3104760c2415Smrg }
3105760c2415Smrg else
3106760c2415Smrg {
3107760c2415Smrg //Bob not found; report the error
3108760c2415Smrg }
3109760c2415Smrg
3110760c2415Smrg //And there's no overhead
3111760c2415Smrg static assert(Nullable!(size_t, size_t.max).sizeof == size_t.sizeof);
3112760c2415Smrg }
3113760c2415Smrg
3114760c2415Smrg ///
3115760c2415Smrg @system unittest
3116760c2415Smrg {
3117760c2415Smrg import std.exception : assertThrown;
3118760c2415Smrg
3119760c2415Smrg Nullable!(int, int.min) a;
3120760c2415Smrg assert(a.isNull);
3121760c2415Smrg assertThrown!Throwable(a.get);
3122760c2415Smrg a = 5;
3123760c2415Smrg assert(!a.isNull);
3124760c2415Smrg assert(a == 5);
3125760c2415Smrg static assert(a.sizeof == int.sizeof);
3126760c2415Smrg }
3127760c2415Smrg
3128760c2415Smrg ///
3129760c2415Smrg @safe unittest
3130760c2415Smrg {
3131760c2415Smrg auto a = nullable!(int.min)(8);
3132760c2415Smrg assert(a == 8);
3133760c2415Smrg a.nullify();
3134760c2415Smrg assert(a.isNull);
3135760c2415Smrg }
3136760c2415Smrg
3137760c2415Smrg @safe unittest
3138760c2415Smrg {
3139760c2415Smrg static int f(in Nullable!(int, int.min) x) {
3140760c2415Smrg return x.isNull ? 42 : x.get;
3141760c2415Smrg }
3142760c2415Smrg Nullable!(int, int.min) a;
3143760c2415Smrg assert(f(a) == 42);
3144760c2415Smrg a = 8;
3145760c2415Smrg assert(f(a) == 8);
3146760c2415Smrg a.nullify();
3147760c2415Smrg assert(f(a) == 42);
3148760c2415Smrg }
3149760c2415Smrg @safe unittest
3150760c2415Smrg {
3151760c2415Smrg // Ensure Nullable can be used in pure/nothrow/@safe environment.
3152760c2415Smrg function() @safe pure nothrow
3153760c2415Smrg {
3154760c2415Smrg Nullable!(int, int.min) n;
3155760c2415Smrg assert(n.isNull);
3156760c2415Smrg n = 4;
3157760c2415Smrg assert(!n.isNull);
3158760c2415Smrg assert(n == 4);
3159760c2415Smrg n.nullify();
3160760c2415Smrg assert(n.isNull);
3161760c2415Smrg }();
3162760c2415Smrg }
3163760c2415Smrg @system unittest
3164760c2415Smrg {
3165760c2415Smrg // Ensure Nullable can be used when the value is not pure/nothrow/@system
3166760c2415Smrg static struct S
3167760c2415Smrg {
3168760c2415Smrg int x;
3169760c2415Smrg bool opEquals(const S s) const @system { return s.x == x; }
3170760c2415Smrg }
3171760c2415Smrg
3172760c2415Smrg Nullable!(S, S(711)) s;
3173760c2415Smrg assert(s.isNull);
3174760c2415Smrg s = S(5);
3175760c2415Smrg assert(!s.isNull);
3176760c2415Smrg assert(s.x == 5);
3177760c2415Smrg s.nullify();
3178760c2415Smrg assert(s.isNull);
3179760c2415Smrg }
3180760c2415Smrg @safe unittest
3181760c2415Smrg {
3182760c2415Smrg //Check nullable is nicelly embedable in a struct
3183760c2415Smrg static struct S1
3184760c2415Smrg {
3185760c2415Smrg Nullable!(int, 0) ni;
3186760c2415Smrg }
3187760c2415Smrg static struct S2 //inspired from 9404
3188760c2415Smrg {
3189760c2415Smrg Nullable!(int, 0) ni;
3190760c2415Smrg this(S2 other)
3191760c2415Smrg {
3192760c2415Smrg ni = other.ni;
3193760c2415Smrg }
3194760c2415Smrg void opAssign(S2 other)
3195760c2415Smrg {
3196760c2415Smrg ni = other.ni;
3197760c2415Smrg }
3198760c2415Smrg }
3199760c2415Smrg foreach (S; AliasSeq!(S1, S2))
3200760c2415Smrg {
3201760c2415Smrg S a;
3202760c2415Smrg S b = a;
3203760c2415Smrg S c;
3204760c2415Smrg c = a;
3205760c2415Smrg }
3206760c2415Smrg }
3207760c2415Smrg @system unittest
3208760c2415Smrg {
3209760c2415Smrg import std.conv : to;
3210760c2415Smrg
3211760c2415Smrg // Bugzilla 10915
3212760c2415Smrg Nullable!(int, 1) ni = 1;
3213760c2415Smrg assert(ni.to!string() == "Nullable.null");
3214760c2415Smrg
3215760c2415Smrg struct Test { string s; }
3216760c2415Smrg alias NullableTest = Nullable!(Test, Test("null"));
3217760c2415Smrg
3218760c2415Smrg NullableTest nt = Test("test");
3219760c2415Smrg assert(nt.to!string() == `Test("test")`);
3220760c2415Smrg
3221760c2415Smrg NullableTest ntn = Test("null");
3222760c2415Smrg assert(ntn.to!string() == "Nullable.null");
3223760c2415Smrg
3224760c2415Smrg class TestToString
3225760c2415Smrg {
3226760c2415Smrg double d;
3227760c2415Smrg
3228760c2415Smrg this(double d)
3229760c2415Smrg {
3230760c2415Smrg this.d = d;
3231760c2415Smrg }
3232760c2415Smrg
3233760c2415Smrg override string toString()
3234760c2415Smrg {
3235760c2415Smrg return d.to!string();
3236760c2415Smrg }
3237760c2415Smrg }
3238760c2415Smrg alias NullableTestToString = Nullable!(TestToString, null);
3239760c2415Smrg
3240760c2415Smrg NullableTestToString ntts = new TestToString(2.5);
3241760c2415Smrg assert(ntts.to!string() == "2.5");
3242760c2415Smrg }
3243760c2415Smrg
3244760c2415Smrg
3245760c2415Smrg /**
3246760c2415Smrg Just like $(D Nullable!T), except that the object refers to a value
3247760c2415Smrg sitting elsewhere in memory. This makes assignments overwrite the
3248760c2415Smrg initially assigned value. Internally $(D NullableRef!T) only stores a
3249760c2415Smrg pointer to $(D T) (i.e., $(D Nullable!T.sizeof == (T*).sizeof)).
3250760c2415Smrg */
3251760c2415Smrg struct NullableRef(T)
3252760c2415Smrg {
3253760c2415Smrg private T* _value;
3254760c2415Smrg
3255760c2415Smrg /**
3256760c2415Smrg Constructor binding $(D this) to $(D value).
3257760c2415Smrg
3258760c2415Smrg Params:
3259760c2415Smrg value = The value to bind to.
3260760c2415Smrg */
3261760c2415Smrg this(T* value) @safe pure nothrow
3262760c2415Smrg {
3263760c2415Smrg _value = value;
3264760c2415Smrg }
3265760c2415Smrg
3266760c2415Smrg template toString()
3267760c2415Smrg {
3268760c2415Smrg import std.format : FormatSpec, formatValue;
3269760c2415Smrg // Needs to be a template because of DMD @@BUG@@ 13737.
3270760c2415Smrg void toString()(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
3271760c2415Smrg {
3272760c2415Smrg if (isNull)
3273760c2415Smrg {
3274760c2415Smrg sink.formatValue("Nullable.null", fmt);
3275760c2415Smrg }
3276760c2415Smrg else
3277760c2415Smrg {
3278760c2415Smrg sink.formatValue(*_value, fmt);
3279760c2415Smrg }
3280760c2415Smrg }
3281760c2415Smrg }
3282760c2415Smrg
3283760c2415Smrg /**
3284760c2415Smrg Binds the internal state to $(D value).
3285760c2415Smrg
3286760c2415Smrg Params:
3287760c2415Smrg value = A pointer to a value of type `T` to bind this `NullableRef` to.
3288760c2415Smrg */
3289760c2415Smrg void bind(T* value) @safe pure nothrow
3290760c2415Smrg {
3291760c2415Smrg _value = value;
3292760c2415Smrg }
3293760c2415Smrg
3294760c2415Smrg ///
3295760c2415Smrg @safe unittest
3296760c2415Smrg {
3297760c2415Smrg NullableRef!int nr = new int(42);
3298760c2415Smrg assert(nr == 42);
3299760c2415Smrg
3300760c2415Smrg int* n = new int(1);
3301760c2415Smrg nr.bind(n);
3302760c2415Smrg assert(nr == 1);
3303760c2415Smrg }
3304760c2415Smrg
3305760c2415Smrg /**
3306760c2415Smrg Returns $(D true) if and only if $(D this) is in the null state.
3307760c2415Smrg
3308760c2415Smrg Returns:
3309760c2415Smrg true if `this` is in the null state, otherwise false.
3310760c2415Smrg */
3311760c2415Smrg @property bool isNull() const @safe pure nothrow
3312760c2415Smrg {
3313760c2415Smrg return _value is null;
3314760c2415Smrg }
3315760c2415Smrg
3316760c2415Smrg ///
3317760c2415Smrg @safe unittest
3318760c2415Smrg {
3319760c2415Smrg NullableRef!int nr;
3320760c2415Smrg assert(nr.isNull);
3321760c2415Smrg
3322760c2415Smrg int* n = new int(42);
3323760c2415Smrg nr.bind(n);
3324760c2415Smrg assert(!nr.isNull && nr == 42);
3325760c2415Smrg }
3326760c2415Smrg
3327760c2415Smrg /**
3328760c2415Smrg Forces $(D this) to the null state.
3329760c2415Smrg */
3330760c2415Smrg void nullify() @safe pure nothrow
3331760c2415Smrg {
3332760c2415Smrg _value = null;
3333760c2415Smrg }
3334760c2415Smrg
3335760c2415Smrg ///
3336760c2415Smrg @safe unittest
3337760c2415Smrg {
3338760c2415Smrg NullableRef!int nr = new int(42);
3339760c2415Smrg assert(!nr.isNull);
3340760c2415Smrg
3341760c2415Smrg nr.nullify();
3342760c2415Smrg assert(nr.isNull);
3343760c2415Smrg }
3344760c2415Smrg
3345760c2415Smrg /**
3346760c2415Smrg Assigns $(D value) to the internally-held state.
3347760c2415Smrg
3348760c2415Smrg Params:
3349760c2415Smrg value = A value of type `T` to assign to this `NullableRef`.
3350760c2415Smrg If the internal state of this `NullableRef` has not
3351760c2415Smrg been initialized, an error will be thrown in
3352760c2415Smrg non-release mode.
3353760c2415Smrg */
3354760c2415Smrg void opAssign()(T value)
3355760c2415Smrg if (isAssignable!T) //@@@9416@@@
3356760c2415Smrg {
3357760c2415Smrg enum message = "Called `opAssign' on null NullableRef!" ~ T.stringof ~ ".";
3358760c2415Smrg assert(!isNull, message);
3359760c2415Smrg *_value = value;
3360760c2415Smrg }
3361760c2415Smrg
3362760c2415Smrg ///
3363760c2415Smrg @system unittest
3364760c2415Smrg {
3365760c2415Smrg import std.exception : assertThrown, assertNotThrown;
3366760c2415Smrg
3367760c2415Smrg NullableRef!int nr;
3368760c2415Smrg assert(nr.isNull);
3369760c2415Smrg assertThrown!Throwable(nr = 42);
3370760c2415Smrg
3371760c2415Smrg nr.bind(new int(0));
3372760c2415Smrg assert(!nr.isNull);
3373760c2415Smrg assertNotThrown!Throwable(nr = 42);
3374760c2415Smrg assert(nr == 42);
3375760c2415Smrg }
3376760c2415Smrg
3377760c2415Smrg /**
3378760c2415Smrg Gets the value. $(D this) must not be in the null state.
3379760c2415Smrg This function is also called for the implicit conversion to $(D T).
3380760c2415Smrg */
3381760c2415Smrg @property ref inout(T) get() inout @safe pure nothrow
3382760c2415Smrg {
3383760c2415Smrg enum message = "Called `get' on null NullableRef!" ~ T.stringof ~ ".";
3384760c2415Smrg assert(!isNull, message);
3385760c2415Smrg return *_value;
3386760c2415Smrg }
3387760c2415Smrg
3388760c2415Smrg ///
3389760c2415Smrg @system unittest
3390760c2415Smrg {
3391760c2415Smrg import std.exception : assertThrown, assertNotThrown;
3392760c2415Smrg
3393760c2415Smrg NullableRef!int nr;
3394760c2415Smrg //`get` is implicitly called. Will throw
3395760c2415Smrg //an error in non-release mode
3396760c2415Smrg assertThrown!Throwable(nr == 0);
3397760c2415Smrg
3398760c2415Smrg nr.bind(new int(0));
3399760c2415Smrg assertNotThrown!Throwable(nr == 0);
3400760c2415Smrg }
3401760c2415Smrg
3402760c2415Smrg /**
3403760c2415Smrg Implicitly converts to $(D T).
3404760c2415Smrg $(D this) must not be in the null state.
3405760c2415Smrg */
3406760c2415Smrg alias get this;
3407760c2415Smrg }
3408760c2415Smrg
3409760c2415Smrg /// ditto
3410760c2415Smrg auto nullableRef(T)(T* t)
3411760c2415Smrg {
3412760c2415Smrg return NullableRef!T(t);
3413760c2415Smrg }
3414760c2415Smrg
3415760c2415Smrg ///
3416760c2415Smrg @system unittest
3417760c2415Smrg {
3418760c2415Smrg import std.exception : assertThrown;
3419760c2415Smrg
3420760c2415Smrg int x = 5, y = 7;
3421760c2415Smrg auto a = nullableRef(&x);
3422760c2415Smrg assert(!a.isNull);
3423760c2415Smrg assert(a == 5);
3424760c2415Smrg assert(x == 5);
3425760c2415Smrg a = 42;
3426760c2415Smrg assert(x == 42);
3427760c2415Smrg assert(!a.isNull);
3428760c2415Smrg assert(a == 42);
3429760c2415Smrg a.nullify();
3430760c2415Smrg assert(x == 42);
3431760c2415Smrg assert(a.isNull);
3432760c2415Smrg assertThrown!Throwable(a.get);
3433760c2415Smrg assertThrown!Throwable(a = 71);
3434760c2415Smrg a.bind(&y);
3435760c2415Smrg assert(a == 7);
3436760c2415Smrg y = 135;
3437760c2415Smrg assert(a == 135);
3438760c2415Smrg }
3439760c2415Smrg @system unittest
3440760c2415Smrg {
3441760c2415Smrg static int f(in NullableRef!int x) {
3442760c2415Smrg return x.isNull ? 42 : x.get;
3443760c2415Smrg }
3444760c2415Smrg int x = 5;
3445760c2415Smrg auto a = nullableRef(&x);
3446760c2415Smrg assert(f(a) == 5);
3447760c2415Smrg a.nullify();
3448760c2415Smrg assert(f(a) == 42);
3449760c2415Smrg }
3450760c2415Smrg @safe unittest
3451760c2415Smrg {
3452760c2415Smrg // Ensure NullableRef can be used in pure/nothrow/@safe environment.
3453760c2415Smrg function() @safe pure nothrow
3454760c2415Smrg {
3455760c2415Smrg auto storage = new int;
3456760c2415Smrg *storage = 19902;
3457760c2415Smrg NullableRef!int n;
3458760c2415Smrg assert(n.isNull);
3459760c2415Smrg n.bind(storage);
3460760c2415Smrg assert(!n.isNull);
3461760c2415Smrg assert(n == 19902);
3462760c2415Smrg n = 2294;
3463760c2415Smrg assert(n == 2294);
3464760c2415Smrg assert(*storage == 2294);
3465760c2415Smrg n.nullify();
3466760c2415Smrg assert(n.isNull);
3467760c2415Smrg }();
3468760c2415Smrg }
3469760c2415Smrg @system unittest
3470760c2415Smrg {
3471760c2415Smrg // Ensure NullableRef can be used when the value is not pure/nothrow/@safe
3472760c2415Smrg static struct S
3473760c2415Smrg {
3474760c2415Smrg int x;
3475760c2415Smrg this(this) @system {}
3476760c2415Smrg bool opEquals(const S s) const @system { return s.x == x; }
3477760c2415Smrg }
3478760c2415Smrg
3479760c2415Smrg auto storage = S(5);
3480760c2415Smrg
3481760c2415Smrg NullableRef!S s;
3482760c2415Smrg assert(s.isNull);
3483760c2415Smrg s.bind(&storage);
3484760c2415Smrg assert(!s.isNull);
3485760c2415Smrg assert(s.x == 5);
3486760c2415Smrg s.nullify();
3487760c2415Smrg assert(s.isNull);
3488760c2415Smrg }
3489760c2415Smrg @safe unittest
3490760c2415Smrg {
3491760c2415Smrg //Check nullable is nicelly embedable in a struct
3492760c2415Smrg static struct S1
3493760c2415Smrg {
3494760c2415Smrg NullableRef!int ni;
3495760c2415Smrg }
3496760c2415Smrg static struct S2 //inspired from 9404
3497760c2415Smrg {
3498760c2415Smrg NullableRef!int ni;
3499760c2415Smrg this(S2 other)
3500760c2415Smrg {
3501760c2415Smrg ni = other.ni;
3502760c2415Smrg }
3503760c2415Smrg void opAssign(S2 other)
3504760c2415Smrg {
3505760c2415Smrg ni = other.ni;
3506760c2415Smrg }
3507760c2415Smrg }
3508760c2415Smrg foreach (S; AliasSeq!(S1, S2))
3509760c2415Smrg {
3510760c2415Smrg S a;
3511760c2415Smrg S b = a;
3512760c2415Smrg S c;
3513760c2415Smrg c = a;
3514760c2415Smrg }
3515760c2415Smrg }
3516760c2415Smrg @system unittest
3517760c2415Smrg {
3518760c2415Smrg import std.conv : to;
3519760c2415Smrg
3520760c2415Smrg // Bugzilla 10915
3521760c2415Smrg NullableRef!int nri;
3522760c2415Smrg assert(nri.to!string() == "Nullable.null");
3523760c2415Smrg
3524760c2415Smrg struct Test
3525760c2415Smrg {
3526760c2415Smrg string s;
3527760c2415Smrg }
3528760c2415Smrg NullableRef!Test nt = new Test("test");
3529760c2415Smrg assert(nt.to!string() == `Test("test")`);
3530760c2415Smrg
3531760c2415Smrg class TestToString
3532760c2415Smrg {
3533760c2415Smrg double d;
3534760c2415Smrg
3535760c2415Smrg this(double d)
3536760c2415Smrg {
3537760c2415Smrg this.d = d;
3538760c2415Smrg }
3539760c2415Smrg
3540760c2415Smrg override string toString()
3541760c2415Smrg {
3542760c2415Smrg return d.to!string();
3543760c2415Smrg }
3544760c2415Smrg }
3545760c2415Smrg TestToString tts = new TestToString(2.5);
3546760c2415Smrg NullableRef!TestToString ntts = &tts;
3547760c2415Smrg assert(ntts.to!string() == "2.5");
3548760c2415Smrg }
3549760c2415Smrg
3550760c2415Smrg
3551760c2415Smrg /**
3552760c2415Smrg $(D BlackHole!Base) is a subclass of $(D Base) which automatically implements
3553760c2415Smrg all abstract member functions in $(D Base) as do-nothing functions. Each
3554760c2415Smrg auto-implemented function just returns the default value of the return type
3555760c2415Smrg without doing anything.
3556760c2415Smrg
3557760c2415Smrg The name came from
3558760c2415Smrg $(HTTP search.cpan.org/~sburke/Class-_BlackHole-0.04/lib/Class/_BlackHole.pm, Class::_BlackHole)
3559760c2415Smrg Perl module by Sean M. Burke.
3560760c2415Smrg
3561760c2415Smrg Params:
3562760c2415Smrg Base = A non-final class for `BlackHole` to inherit from.
3563760c2415Smrg
3564760c2415Smrg See_Also:
3565760c2415Smrg $(LREF AutoImplement), $(LREF generateEmptyFunction)
3566760c2415Smrg */
3567760c2415Smrg alias BlackHole(Base) = AutoImplement!(Base, generateEmptyFunction, isAbstractFunction);
3568760c2415Smrg
3569760c2415Smrg ///
3570760c2415Smrg @system unittest
3571760c2415Smrg {
3572760c2415Smrg import std.math : isNaN;
3573760c2415Smrg
3574760c2415Smrg static abstract class C
3575760c2415Smrg {
3576760c2415Smrg int m_value;
3577760c2415Smrg this(int v) { m_value = v; }
3578760c2415Smrg int value() @property { return m_value; }
3579760c2415Smrg
3580760c2415Smrg abstract real realValue() @property;
3581760c2415Smrg abstract void doSomething();
3582760c2415Smrg }
3583760c2415Smrg
3584760c2415Smrg auto c = new BlackHole!C(42);
3585760c2415Smrg assert(c.value == 42);
3586760c2415Smrg
3587760c2415Smrg // Returns real.init which is NaN
3588760c2415Smrg assert(c.realValue.isNaN);
3589760c2415Smrg // Abstract functions are implemented as do-nothing
3590760c2415Smrg c.doSomething();
3591760c2415Smrg }
3592760c2415Smrg
3593760c2415Smrg @system unittest
3594760c2415Smrg {
3595760c2415Smrg import std.math : isNaN;
3596760c2415Smrg
3597760c2415Smrg // return default
3598760c2415Smrg {
3599760c2415Smrg interface I_1 { real test(); }
3600760c2415Smrg auto o = new BlackHole!I_1;
3601760c2415Smrg assert(o.test().isNaN()); // NaN
3602760c2415Smrg }
3603760c2415Smrg // doc example
3604760c2415Smrg {
3605760c2415Smrg static class C
3606760c2415Smrg {
3607760c2415Smrg int m_value;
3608760c2415Smrg this(int v) { m_value = v; }
3609760c2415Smrg int value() @property { return m_value; }
3610760c2415Smrg
3611760c2415Smrg abstract real realValue() @property;
3612760c2415Smrg abstract void doSomething();
3613760c2415Smrg }
3614760c2415Smrg
3615760c2415Smrg auto c = new BlackHole!C(42);
3616760c2415Smrg assert(c.value == 42);
3617760c2415Smrg
3618760c2415Smrg assert(c.realValue.isNaN); // NaN
3619760c2415Smrg c.doSomething();
3620760c2415Smrg }
3621760c2415Smrg
3622760c2415Smrg // Bugzilla 12058
3623760c2415Smrg interface Foo
3624760c2415Smrg {
3625760c2415Smrg inout(Object) foo() inout;
3626760c2415Smrg }
3627760c2415Smrg BlackHole!Foo o;
3628760c2415Smrg }
3629760c2415Smrg
3630760c2415Smrg
3631760c2415Smrg /**
3632760c2415Smrg $(D WhiteHole!Base) is a subclass of $(D Base) which automatically implements
3633760c2415Smrg all abstract member functions as functions that always fail. These functions
3634760c2415Smrg simply throw an $(D Error) and never return. `Whitehole` is useful for
3635760c2415Smrg trapping the use of class member functions that haven't been implemented.
3636760c2415Smrg
3637760c2415Smrg The name came from
3638760c2415Smrg $(HTTP search.cpan.org/~mschwern/Class-_WhiteHole-0.04/lib/Class/_WhiteHole.pm, Class::_WhiteHole)
3639760c2415Smrg Perl module by Michael G Schwern.
3640760c2415Smrg
3641760c2415Smrg Params:
3642760c2415Smrg Base = A non-final class for `WhiteHole` to inherit from.
3643760c2415Smrg
3644760c2415Smrg See_Also:
3645760c2415Smrg $(LREF AutoImplement), $(LREF generateAssertTrap)
3646760c2415Smrg */
3647760c2415Smrg alias WhiteHole(Base) = AutoImplement!(Base, generateAssertTrap, isAbstractFunction);
3648760c2415Smrg
3649760c2415Smrg ///
3650760c2415Smrg @system unittest
3651760c2415Smrg {
3652760c2415Smrg import std.exception : assertThrown;
3653760c2415Smrg
3654760c2415Smrg static class C
3655760c2415Smrg {
3656760c2415Smrg abstract void notYetImplemented();
3657760c2415Smrg }
3658760c2415Smrg
3659760c2415Smrg auto c = new WhiteHole!C;
3660760c2415Smrg assertThrown!NotImplementedError(c.notYetImplemented()); // throws an Error
3661760c2415Smrg }
3662760c2415Smrg
3663760c2415Smrg // / ditto
3664760c2415Smrg class NotImplementedError : Error
3665760c2415Smrg {
3666760c2415Smrg this(string method)
3667760c2415Smrg {
3668760c2415Smrg super(method ~ " is not implemented");
3669760c2415Smrg }
3670760c2415Smrg }
3671760c2415Smrg
3672760c2415Smrg @system unittest
3673760c2415Smrg {
3674760c2415Smrg import std.exception : assertThrown;
3675760c2415Smrg // nothrow
3676760c2415Smrg {
3677760c2415Smrg interface I_1
3678760c2415Smrg {
3679760c2415Smrg void foo();
3680760c2415Smrg void bar() nothrow;
3681760c2415Smrg }
3682760c2415Smrg auto o = new WhiteHole!I_1;
3683760c2415Smrg assertThrown!NotImplementedError(o.foo());
3684760c2415Smrg assertThrown!NotImplementedError(o.bar());
3685760c2415Smrg }
3686760c2415Smrg // doc example
3687760c2415Smrg {
3688760c2415Smrg static class C
3689760c2415Smrg {
3690760c2415Smrg abstract void notYetImplemented();
3691760c2415Smrg }
3692760c2415Smrg
3693760c2415Smrg auto c = new WhiteHole!C;
3694760c2415Smrg try
3695760c2415Smrg {
3696760c2415Smrg c.notYetImplemented();
3697760c2415Smrg assert(0);
3698760c2415Smrg }
3699760c2415Smrg catch (Error e) {}
3700760c2415Smrg }
3701760c2415Smrg }
3702760c2415Smrg
3703760c2415Smrg
3704760c2415Smrg /**
3705760c2415Smrg $(D AutoImplement) automatically implements (by default) all abstract member
3706760c2415Smrg functions in the class or interface $(D Base) in specified way.
3707760c2415Smrg
3708760c2415Smrg The second version of $(D AutoImplement) automatically implements
3709760c2415Smrg $(D Interface), while deriving from $(D BaseClass).
3710760c2415Smrg
3711760c2415Smrg Params:
3712760c2415Smrg how = template which specifies _how functions will be implemented/overridden.
3713760c2415Smrg
3714760c2415Smrg Two arguments are passed to $(D how): the type $(D Base) and an alias
3715760c2415Smrg to an implemented function. Then $(D how) must return an implemented
3716760c2415Smrg function body as a string.
3717760c2415Smrg
3718760c2415Smrg The generated function body can use these keywords:
3719760c2415Smrg $(UL
3720760c2415Smrg $(LI $(D a0), $(D a1), …: arguments passed to the function;)
3721760c2415Smrg $(LI $(D args): a tuple of the arguments;)
3722760c2415Smrg $(LI $(D self): an alias to the function itself;)
3723760c2415Smrg $(LI $(D parent): an alias to the overridden function (if any).)
3724760c2415Smrg )
3725760c2415Smrg
3726760c2415Smrg You may want to use templated property functions (instead of Implicit
3727760c2415Smrg Template Properties) to generate complex functions:
3728760c2415Smrg --------------------
3729760c2415Smrg // Prints log messages for each call to overridden functions.
3730760c2415Smrg string generateLogger(C, alias fun)() @property
3731760c2415Smrg {
3732760c2415Smrg import std.traits;
3733760c2415Smrg enum qname = C.stringof ~ "." ~ __traits(identifier, fun);
3734760c2415Smrg string stmt;
3735760c2415Smrg
3736760c2415Smrg stmt ~= q{ struct Importer { import std.stdio; } };
3737760c2415Smrg stmt ~= `Importer.writeln("Log: ` ~ qname ~ `(", args, ")");`;
3738760c2415Smrg static if (!__traits(isAbstractFunction, fun))
3739760c2415Smrg {
3740760c2415Smrg static if (is(ReturnType!fun == void))
3741760c2415Smrg stmt ~= q{ parent(args); };
3742760c2415Smrg else
3743760c2415Smrg stmt ~= q{
3744760c2415Smrg auto r = parent(args);
3745760c2415Smrg Importer.writeln("--> ", r);
3746760c2415Smrg return r;
3747760c2415Smrg };
3748760c2415Smrg }
3749760c2415Smrg return stmt;
3750760c2415Smrg }
3751760c2415Smrg --------------------
3752760c2415Smrg
3753760c2415Smrg what = template which determines _what functions should be
3754760c2415Smrg implemented/overridden.
3755760c2415Smrg
3756760c2415Smrg An argument is passed to $(D what): an alias to a non-final member
3757760c2415Smrg function in $(D Base). Then $(D what) must return a boolean value.
3758760c2415Smrg Return $(D true) to indicate that the passed function should be
3759760c2415Smrg implemented/overridden.
3760760c2415Smrg
3761760c2415Smrg --------------------
3762760c2415Smrg // Sees if fun returns something.
3763760c2415Smrg enum bool hasValue(alias fun) = !is(ReturnType!(fun) == void);
3764760c2415Smrg --------------------
3765760c2415Smrg
3766760c2415Smrg
3767760c2415Smrg Note:
3768760c2415Smrg
3769760c2415Smrg Generated code is inserted in the scope of $(D std.typecons) module. Thus,
3770760c2415Smrg any useful functions outside $(D std.typecons) cannot be used in the generated
3771760c2415Smrg code. To workaround this problem, you may $(D import) necessary things in a
3772760c2415Smrg local struct, as done in the $(D generateLogger()) template in the above
3773760c2415Smrg example.
3774760c2415Smrg
3775760c2415Smrg
3776760c2415Smrg BUGS:
3777760c2415Smrg
3778760c2415Smrg $(UL
3779760c2415Smrg $(LI Variadic arguments to constructors are not forwarded to super.)
3780760c2415Smrg $(LI Deep interface inheritance causes compile error with messages like
3781760c2415Smrg "Error: function std.typecons._AutoImplement!(Foo)._AutoImplement.bar
3782760c2415Smrg does not override any function". [$(BUGZILLA 2525), $(BUGZILLA 3525)] )
3783760c2415Smrg $(LI The $(D parent) keyword is actually a delegate to the super class'
3784760c2415Smrg corresponding member function. [$(BUGZILLA 2540)] )
3785760c2415Smrg $(LI Using alias template parameter in $(D how) and/or $(D what) may cause
3786760c2415Smrg strange compile error. Use template tuple parameter instead to workaround
3787760c2415Smrg this problem. [$(BUGZILLA 4217)] )
3788760c2415Smrg )
3789760c2415Smrg */
3790760c2415Smrg class AutoImplement(Base, alias how, alias what = isAbstractFunction) : Base
3791760c2415Smrg if (!is(how == class))
3792760c2415Smrg {
3793760c2415Smrg private alias autoImplement_helper_ =
3794760c2415Smrg AutoImplement_Helper!("autoImplement_helper_", "Base", Base, typeof(this), how, what);
3795760c2415Smrg mixin(autoImplement_helper_.code);
3796760c2415Smrg }
3797760c2415Smrg
3798760c2415Smrg /// ditto
3799760c2415Smrg class AutoImplement(
3800760c2415Smrg Interface, BaseClass, alias how,
3801760c2415Smrg alias what = isAbstractFunction) : BaseClass, Interface
3802760c2415Smrg if (is(Interface == interface) && is(BaseClass == class))
3803760c2415Smrg {
3804760c2415Smrg private alias autoImplement_helper_ = AutoImplement_Helper!(
3805760c2415Smrg "autoImplement_helper_", "Interface", Interface, typeof(this), how, what);
3806760c2415Smrg mixin(autoImplement_helper_.code);
3807760c2415Smrg }
3808760c2415Smrg
3809760c2415Smrg /*
3810760c2415Smrg * Code-generating stuffs are encupsulated in this helper template so that
3811760c2415Smrg * namespace pollution, which can cause name confliction with Base's public
3812760c2415Smrg * members, should be minimized.
3813760c2415Smrg */
3814760c2415Smrg private template AutoImplement_Helper(string myName, string baseName,
3815760c2415Smrg Base, Self, alias generateMethodBody, alias cherrypickMethod)
3816760c2415Smrg {
3817760c2415Smrg private static:
3818760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3819760c2415Smrg // Internal stuffs
3820760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3821760c2415Smrg
3822760c2415Smrg // Returns function overload sets in the class C, filtered with pred.
3823760c2415Smrg template enumerateOverloads(C, alias pred)
3824760c2415Smrg {
3825760c2415Smrg template Impl(names...)
3826760c2415Smrg {
3827760c2415Smrg import std.meta : Filter;
3828760c2415Smrg static if (names.length > 0)
3829760c2415Smrg {
3830760c2415Smrg alias methods = Filter!(pred, MemberFunctionsTuple!(C, names[0]));
3831760c2415Smrg alias next = Impl!(names[1 .. $]);
3832760c2415Smrg
3833760c2415Smrg static if (methods.length > 0)
3834760c2415Smrg alias Impl = AliasSeq!(OverloadSet!(names[0], methods), next);
3835760c2415Smrg else
3836760c2415Smrg alias Impl = next;
3837760c2415Smrg }
3838760c2415Smrg else
3839760c2415Smrg alias Impl = AliasSeq!();
3840760c2415Smrg }
3841760c2415Smrg
3842760c2415Smrg alias enumerateOverloads = Impl!(__traits(allMembers, C));
3843760c2415Smrg }
3844760c2415Smrg
3845760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3846760c2415Smrg // Target functions
3847760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3848760c2415Smrg
3849760c2415Smrg // Add a non-final check to the cherrypickMethod.
3850760c2415Smrg enum bool canonicalPicker(fun.../+[BUG 4217]+/) =
3851760c2415Smrg !__traits(isFinalFunction, fun[0]) && cherrypickMethod!(fun);
3852760c2415Smrg
3853760c2415Smrg /*
3854760c2415Smrg * A tuple of overload sets, each item of which consists of functions to be
3855760c2415Smrg * implemented by the generated code.
3856760c2415Smrg */
3857760c2415Smrg alias targetOverloadSets = enumerateOverloads!(Base, canonicalPicker);
3858760c2415Smrg
3859760c2415Smrg /*
3860760c2415Smrg * Super class of this AutoImplement instance
3861760c2415Smrg */
3862760c2415Smrg alias Super = BaseTypeTuple!(Self)[0];
3863760c2415Smrg static assert(is(Super == class));
3864760c2415Smrg static assert(is(Base == interface) || is(Super == Base));
3865760c2415Smrg
3866760c2415Smrg /*
3867760c2415Smrg * A tuple of the super class' constructors. Used for forwarding
3868760c2415Smrg * constructor calls.
3869760c2415Smrg */
3870760c2415Smrg static if (__traits(hasMember, Super, "__ctor"))
3871760c2415Smrg alias ctorOverloadSet = OverloadSet!("__ctor", __traits(getOverloads, Super, "__ctor"));
3872760c2415Smrg else
3873760c2415Smrg alias ctorOverloadSet = OverloadSet!("__ctor"); // empty
3874760c2415Smrg
3875760c2415Smrg
3876760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3877760c2415Smrg // Type information
3878760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3879760c2415Smrg
3880760c2415Smrg /*
3881760c2415Smrg * The generated code will be mixed into AutoImplement, which will be
3882760c2415Smrg * instantiated in this module's scope. Thus, any user-defined types are
3883760c2415Smrg * out of scope and cannot be used directly (i.e. by their names).
3884760c2415Smrg *
3885760c2415Smrg * We will use FuncInfo instances for accessing return types and parameter
3886760c2415Smrg * types of the implemented functions. The instances will be populated to
3887760c2415Smrg * the AutoImplement's scope in a certain way; see the populate() below.
3888760c2415Smrg */
3889760c2415Smrg
3890760c2415Smrg // Returns the preferred identifier for the FuncInfo instance for the i-th
3891760c2415Smrg // overloaded function with the name.
3892760c2415Smrg template INTERNAL_FUNCINFO_ID(string name, size_t i)
3893760c2415Smrg {
3894760c2415Smrg import std.format : format;
3895760c2415Smrg
3896760c2415Smrg enum string INTERNAL_FUNCINFO_ID = format("F_%s_%s", name, i);
3897760c2415Smrg }
3898760c2415Smrg
3899760c2415Smrg /*
3900760c2415Smrg * Insert FuncInfo instances about all the target functions here. This
3901760c2415Smrg * enables the generated code to access type information via, for example,
3902760c2415Smrg * "autoImplement_helper_.F_foo_1".
3903760c2415Smrg */
3904760c2415Smrg template populate(overloads...)
3905760c2415Smrg {
3906760c2415Smrg static if (overloads.length > 0)
3907760c2415Smrg {
3908760c2415Smrg mixin populate!(overloads[0].name, overloads[0].contents);
3909760c2415Smrg mixin populate!(overloads[1 .. $]);
3910760c2415Smrg }
3911760c2415Smrg }
3912760c2415Smrg template populate(string name, methods...)
3913760c2415Smrg {
3914760c2415Smrg static if (methods.length > 0)
3915760c2415Smrg {
3916760c2415Smrg mixin populate!(name, methods[0 .. $ - 1]);
3917760c2415Smrg //
3918760c2415Smrg alias target = methods[$ - 1];
3919760c2415Smrg enum ith = methods.length - 1;
3920760c2415Smrg mixin("alias " ~ INTERNAL_FUNCINFO_ID!(name, ith) ~ " = FuncInfo!target;");
3921760c2415Smrg }
3922760c2415Smrg }
3923760c2415Smrg
3924760c2415Smrg public mixin populate!(targetOverloadSets);
3925760c2415Smrg public mixin populate!( ctorOverloadSet );
3926760c2415Smrg
3927760c2415Smrg
3928760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3929760c2415Smrg // Code-generating policies
3930760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3931760c2415Smrg
3932760c2415Smrg /* Common policy configurations for generating constructors and methods. */
3933760c2415Smrg template CommonGeneratingPolicy()
3934760c2415Smrg {
3935760c2415Smrg // base class identifier which generated code should use
3936760c2415Smrg enum string BASE_CLASS_ID = baseName;
3937760c2415Smrg
3938760c2415Smrg // FuncInfo instance identifier which generated code should use
3939760c2415Smrg template FUNCINFO_ID(string name, size_t i)
3940760c2415Smrg {
3941760c2415Smrg enum string FUNCINFO_ID =
3942760c2415Smrg myName ~ "." ~ INTERNAL_FUNCINFO_ID!(name, i);
3943760c2415Smrg }
3944760c2415Smrg }
3945760c2415Smrg
3946760c2415Smrg /* Policy configurations for generating constructors. */
3947760c2415Smrg template ConstructorGeneratingPolicy()
3948760c2415Smrg {
3949760c2415Smrg mixin CommonGeneratingPolicy;
3950760c2415Smrg
3951760c2415Smrg /* Generates constructor body. Just forward to the base class' one. */
3952760c2415Smrg string generateFunctionBody(ctor.../+[BUG 4217]+/)() @property
3953760c2415Smrg {
3954760c2415Smrg enum varstyle = variadicFunctionStyle!(typeof(&ctor[0]));
3955760c2415Smrg
3956760c2415Smrg static if (varstyle & (Variadic.c | Variadic.d))
3957760c2415Smrg {
3958760c2415Smrg // the argptr-forwarding problem
3959760c2415Smrg //pragma(msg, "Warning: AutoImplement!(", Base, ") ",
3960760c2415Smrg // "ignored variadic arguments to the constructor ",
3961760c2415Smrg // FunctionTypeOf!(typeof(&ctor[0])) );
3962760c2415Smrg }
3963760c2415Smrg return "super(args);";
3964760c2415Smrg }
3965760c2415Smrg }
3966760c2415Smrg
3967760c2415Smrg /* Policy configurations for genearting target methods. */
3968760c2415Smrg template MethodGeneratingPolicy()
3969760c2415Smrg {
3970760c2415Smrg mixin CommonGeneratingPolicy;
3971760c2415Smrg
3972760c2415Smrg /* Geneartes method body. */
3973760c2415Smrg string generateFunctionBody(func.../+[BUG 4217]+/)() @property
3974760c2415Smrg {
3975760c2415Smrg return generateMethodBody!(Base, func); // given
3976760c2415Smrg }
3977760c2415Smrg }
3978760c2415Smrg
3979760c2415Smrg
3980760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3981760c2415Smrg // Generated code
3982760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
3983760c2415Smrg
3984760c2415Smrg alias ConstructorGenerator = MemberFunctionGenerator!(ConstructorGeneratingPolicy!());
3985760c2415Smrg alias MethodGenerator = MemberFunctionGenerator!(MethodGeneratingPolicy!());
3986760c2415Smrg
3987760c2415Smrg public enum string code =
3988760c2415Smrg ConstructorGenerator.generateCode!( ctorOverloadSet ) ~ "\n" ~
3989760c2415Smrg MethodGenerator.generateCode!(targetOverloadSets);
3990760c2415Smrg
3991760c2415Smrg debug (SHOW_GENERATED_CODE)
3992760c2415Smrg {
3993760c2415Smrg pragma(msg, "-------------------- < ", Base, " >");
3994760c2415Smrg pragma(msg, code);
3995760c2415Smrg pragma(msg, "--------------------");
3996760c2415Smrg }
3997760c2415Smrg }
3998760c2415Smrg
3999760c2415Smrg //debug = SHOW_GENERATED_CODE;
4000760c2415Smrg @system unittest
4001760c2415Smrg {
4002760c2415Smrg import core.vararg;
4003760c2415Smrg // no function to implement
4004760c2415Smrg {
4005760c2415Smrg interface I_1 {}
4006760c2415Smrg auto o = new BlackHole!I_1;
4007760c2415Smrg }
4008760c2415Smrg // parameters
4009760c2415Smrg {
4010760c2415Smrg interface I_3 { void test(int, in int, out int, ref int, lazy int); }
4011760c2415Smrg auto o = new BlackHole!I_3;
4012760c2415Smrg }
4013760c2415Smrg // use of user-defined type
4014760c2415Smrg {
4015760c2415Smrg struct S {}
4016760c2415Smrg interface I_4 { S test(); }
4017760c2415Smrg auto o = new BlackHole!I_4;
4018760c2415Smrg }
4019760c2415Smrg // overloads
4020760c2415Smrg {
4021760c2415Smrg interface I_5
4022760c2415Smrg {
4023760c2415Smrg void test(string);
4024760c2415Smrg real test(real);
4025760c2415Smrg int test();
4026760c2415Smrg }
4027760c2415Smrg auto o = new BlackHole!I_5;
4028760c2415Smrg }
4029760c2415Smrg // constructor forwarding
4030760c2415Smrg {
4031760c2415Smrg static class C_6
4032760c2415Smrg {
4033760c2415Smrg this(int n) { assert(n == 42); }
4034760c2415Smrg this(string s) { assert(s == "Deeee"); }
4035760c2415Smrg this(...) {}
4036760c2415Smrg }
4037760c2415Smrg auto o1 = new BlackHole!C_6(42);
4038760c2415Smrg auto o2 = new BlackHole!C_6("Deeee");
4039760c2415Smrg auto o3 = new BlackHole!C_6(1, 2, 3, 4);
4040760c2415Smrg }
4041760c2415Smrg // attributes
4042760c2415Smrg {
4043760c2415Smrg interface I_7
4044760c2415Smrg {
4045760c2415Smrg ref int test_ref();
4046760c2415Smrg int test_pure() pure;
4047760c2415Smrg int test_nothrow() nothrow;
4048760c2415Smrg int test_property() @property;
4049760c2415Smrg int test_safe() @safe;
4050760c2415Smrg int test_trusted() @trusted;
4051760c2415Smrg int test_system() @system;
4052760c2415Smrg int test_pure_nothrow() pure nothrow;
4053760c2415Smrg }
4054760c2415Smrg auto o = new BlackHole!I_7;
4055760c2415Smrg }
4056760c2415Smrg // storage classes
4057760c2415Smrg {
4058760c2415Smrg interface I_8
4059760c2415Smrg {
4060760c2415Smrg void test_const() const;
4061760c2415Smrg void test_immutable() immutable;
4062760c2415Smrg void test_shared() shared;
4063760c2415Smrg void test_shared_const() shared const;
4064760c2415Smrg }
4065760c2415Smrg auto o = new BlackHole!I_8;
4066760c2415Smrg }
4067760c2415Smrg // use baseclass
4068760c2415Smrg {
4069760c2415Smrg static class C_9
4070760c2415Smrg {
4071760c2415Smrg private string foo_;
4072760c2415Smrg
4073760c2415Smrg this(string s) {
4074760c2415Smrg foo_ = s;
4075760c2415Smrg }
4076760c2415Smrg
4077760c2415Smrg protected string boilerplate() @property
4078760c2415Smrg {
4079760c2415Smrg return "Boilerplate stuff.";
4080760c2415Smrg }
4081760c2415Smrg
4082760c2415Smrg public string foo() @property
4083760c2415Smrg {
4084760c2415Smrg return foo_;
4085760c2415Smrg }
4086760c2415Smrg }
4087760c2415Smrg
4088760c2415Smrg interface I_10
4089760c2415Smrg {
4090760c2415Smrg string testMethod(size_t);
4091760c2415Smrg }
4092760c2415Smrg
4093760c2415Smrg static string generateTestMethod(C, alias fun)() @property
4094760c2415Smrg {
4095760c2415Smrg return "return this.boilerplate[0 .. a0];";
4096760c2415Smrg }
4097760c2415Smrg
4098760c2415Smrg auto o = new AutoImplement!(I_10, C_9, generateTestMethod)("Testing");
4099760c2415Smrg assert(o.testMethod(11) == "Boilerplate");
4100760c2415Smrg assert(o.foo == "Testing");
4101760c2415Smrg }
4102760c2415Smrg /+ // deep inheritance
4103760c2415Smrg {
4104760c2415Smrg // XXX [BUG 2525,3525]
4105760c2415Smrg // NOTE: [r494] func.c(504-571) FuncDeclaration::semantic()
4106760c2415Smrg interface I { void foo(); }
4107760c2415Smrg interface J : I {}
4108760c2415Smrg interface K : J {}
4109760c2415Smrg static abstract class C_9 : K {}
4110760c2415Smrg auto o = new BlackHole!C_9;
4111760c2415Smrg }+/
4112760c2415Smrg }
4113760c2415Smrg
4114760c2415Smrg // Issue 17177 - AutoImplement fails on function overload sets with "cannot infer type from overloaded function symbol"
4115760c2415Smrg @system unittest
4116760c2415Smrg {
4117760c2415Smrg static class Issue17177
4118760c2415Smrg {
4119760c2415Smrg private string n_;
4120760c2415Smrg
4121760c2415Smrg public {
4122760c2415Smrg Issue17177 overloaded(string n)
4123760c2415Smrg {
4124760c2415Smrg this.n_ = n;
4125760c2415Smrg
4126760c2415Smrg return this;
4127760c2415Smrg }
4128760c2415Smrg
4129760c2415Smrg string overloaded()
4130760c2415Smrg {
4131760c2415Smrg return this.n_;
4132760c2415Smrg }
4133760c2415Smrg }
4134760c2415Smrg }
4135760c2415Smrg
4136760c2415Smrg static string how(C, alias fun)()
4137760c2415Smrg {
4138760c2415Smrg static if (!is(ReturnType!fun == void))
4139760c2415Smrg {
4140760c2415Smrg return q{
4141760c2415Smrg return parent(args);
4142760c2415Smrg };
4143760c2415Smrg }
4144760c2415Smrg else
4145760c2415Smrg {
4146760c2415Smrg return q{
4147760c2415Smrg parent(args);
4148760c2415Smrg };
4149760c2415Smrg }
4150760c2415Smrg }
4151760c2415Smrg
4152760c2415Smrg alias Implementation = AutoImplement!(Issue17177, how, templateNot!isFinalFunction);
4153760c2415Smrg }
4154760c2415Smrg
4155760c2415Smrg version (unittest)
4156760c2415Smrg {
4157760c2415Smrg // Issue 10647
4158760c2415Smrg // Add prefix "issue10647_" as a workaround for issue 1238
4159760c2415Smrg private string issue10647_generateDoNothing(C, alias fun)() @property
4160760c2415Smrg {
4161760c2415Smrg string stmt;
4162760c2415Smrg
4163760c2415Smrg static if (is(ReturnType!fun == void))
4164760c2415Smrg stmt ~= "";
4165760c2415Smrg else
4166760c2415Smrg {
4167760c2415Smrg string returnType = ReturnType!fun.stringof;
4168760c2415Smrg stmt ~= "return "~returnType~".init;";
4169760c2415Smrg }
4170760c2415Smrg return stmt;
4171760c2415Smrg }
4172760c2415Smrg
4173760c2415Smrg private template issue10647_isAlwaysTrue(alias fun)
4174760c2415Smrg {
4175760c2415Smrg enum issue10647_isAlwaysTrue = true;
4176760c2415Smrg }
4177760c2415Smrg
4178760c2415Smrg // Do nothing template
4179760c2415Smrg private template issue10647_DoNothing(Base)
4180760c2415Smrg {
4181760c2415Smrg alias issue10647_DoNothing = AutoImplement!(Base, issue10647_generateDoNothing, issue10647_isAlwaysTrue);
4182760c2415Smrg }
4183760c2415Smrg
4184760c2415Smrg // A class to be overridden
4185760c2415Smrg private class issue10647_Foo{
4186760c2415Smrg void bar(int a) { }
4187760c2415Smrg }
4188760c2415Smrg }
4189760c2415Smrg @system unittest
4190760c2415Smrg {
4191760c2415Smrg auto foo = new issue10647_DoNothing!issue10647_Foo();
4192760c2415Smrg foo.bar(13);
4193760c2415Smrg }
4194760c2415Smrg
4195760c2415Smrg /*
4196760c2415Smrg Used by MemberFunctionGenerator.
4197760c2415Smrg */
4198760c2415Smrg package template OverloadSet(string nam, T...)
4199760c2415Smrg {
4200760c2415Smrg enum string name = nam;
4201760c2415Smrg alias contents = T;
4202760c2415Smrg }
4203760c2415Smrg
4204760c2415Smrg /*
4205760c2415Smrg Used by MemberFunctionGenerator.
4206760c2415Smrg */
4207760c2415Smrg package template FuncInfo(alias func, /+[BUG 4217 ?]+/ T = typeof(&func))
4208760c2415Smrg {
4209760c2415Smrg alias RT = ReturnType!T;
4210760c2415Smrg alias PT = Parameters!T;
4211760c2415Smrg }
4212760c2415Smrg package template FuncInfo(Func)
4213760c2415Smrg {
4214760c2415Smrg alias RT = ReturnType!Func;
4215760c2415Smrg alias PT = Parameters!Func;
4216760c2415Smrg }
4217760c2415Smrg
4218760c2415Smrg /*
4219760c2415Smrg General-purpose member function generator.
4220760c2415Smrg --------------------
4221760c2415Smrg template GeneratingPolicy()
4222760c2415Smrg {
4223760c2415Smrg // [optional] the name of the class where functions are derived
4224760c2415Smrg enum string BASE_CLASS_ID;
4225760c2415Smrg
4226760c2415Smrg // [optional] define this if you have only function types
4227760c2415Smrg enum bool WITHOUT_SYMBOL;
4228760c2415Smrg
4229760c2415Smrg // [optional] Returns preferred identifier for i-th parameter.
4230760c2415Smrg template PARAMETER_VARIABLE_ID(size_t i);
4231760c2415Smrg
4232760c2415Smrg // Returns the identifier of the FuncInfo instance for the i-th overload
4233760c2415Smrg // of the specified name. The identifier must be accessible in the scope
4234760c2415Smrg // where generated code is mixed.
4235760c2415Smrg template FUNCINFO_ID(string name, size_t i);
4236760c2415Smrg
4237760c2415Smrg // Returns implemented function body as a string. When WITHOUT_SYMBOL is
4238760c2415Smrg // defined, the latter is used.
4239760c2415Smrg template generateFunctionBody(alias func);
4240760c2415Smrg template generateFunctionBody(string name, FuncType);
4241760c2415Smrg }
4242760c2415Smrg --------------------
4243760c2415Smrg */
4244760c2415Smrg package template MemberFunctionGenerator(alias Policy)
4245760c2415Smrg {
4246760c2415Smrg private static:
4247760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
4248760c2415Smrg // Internal stuffs
4249760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
4250760c2415Smrg import std.format;
4251760c2415Smrg
4252760c2415Smrg enum CONSTRUCTOR_NAME = "__ctor";
4253760c2415Smrg
4254760c2415Smrg // true if functions are derived from a base class
4255760c2415Smrg enum WITH_BASE_CLASS = __traits(hasMember, Policy, "BASE_CLASS_ID");
4256760c2415Smrg
4257760c2415Smrg // true if functions are specified as types, not symbols
4258760c2415Smrg enum WITHOUT_SYMBOL = __traits(hasMember, Policy, "WITHOUT_SYMBOL");
4259760c2415Smrg
4260760c2415Smrg // preferred identifier for i-th parameter variable
4261760c2415Smrg static if (__traits(hasMember, Policy, "PARAMETER_VARIABLE_ID"))
4262760c2415Smrg {
4263760c2415Smrg alias PARAMETER_VARIABLE_ID = Policy.PARAMETER_VARIABLE_ID;
4264760c2415Smrg }
4265760c2415Smrg else
4266760c2415Smrg {
4267760c2415Smrg enum string PARAMETER_VARIABLE_ID(size_t i) = format("a%s", i);
4268760c2415Smrg // default: a0, a1, ...
4269760c2415Smrg }
4270760c2415Smrg
4271760c2415Smrg // Returns a tuple consisting of 0,1,2,...,n-1. For static foreach.
4272760c2415Smrg template CountUp(size_t n)
4273760c2415Smrg {
4274760c2415Smrg static if (n > 0)
4275760c2415Smrg alias CountUp = AliasSeq!(CountUp!(n - 1), n - 1);
4276760c2415Smrg else
4277760c2415Smrg alias CountUp = AliasSeq!();
4278760c2415Smrg }
4279760c2415Smrg
4280760c2415Smrg
4281760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
4282760c2415Smrg // Code generator
4283760c2415Smrg //:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::://
4284760c2415Smrg
4285760c2415Smrg /*
4286760c2415Smrg * Runs through all the target overload sets and generates D code which
4287760c2415Smrg * implements all the functions in the overload sets.
4288760c2415Smrg */
4289760c2415Smrg public string generateCode(overloads...)() @property
4290760c2415Smrg {
4291760c2415Smrg string code = "";
4292760c2415Smrg
4293760c2415Smrg // run through all the overload sets
4294760c2415Smrg foreach (i_; CountUp!(0 + overloads.length)) // workaround
4295760c2415Smrg {
4296760c2415Smrg enum i = 0 + i_; // workaround
4297760c2415Smrg alias oset = overloads[i];
4298760c2415Smrg
4299760c2415Smrg code ~= generateCodeForOverloadSet!(oset);
4300760c2415Smrg
4301760c2415Smrg static if (WITH_BASE_CLASS && oset.name != CONSTRUCTOR_NAME)
4302760c2415Smrg {
4303760c2415Smrg // The generated function declarations may hide existing ones
4304760c2415Smrg // in the base class (cf. HiddenFuncError), so we put an alias
4305760c2415Smrg // declaration here to reveal possible hidden functions.
4306760c2415Smrg code ~= format("alias %s = %s.%s;\n",
4307760c2415Smrg oset.name,
4308760c2415Smrg Policy.BASE_CLASS_ID, // [BUG 2540] super.
4309760c2415Smrg oset.name);
4310760c2415Smrg }
4311760c2415Smrg }
4312760c2415Smrg return code;
4313760c2415Smrg }
4314760c2415Smrg
4315760c2415Smrg // handle each overload set
4316760c2415Smrg private string generateCodeForOverloadSet(alias oset)() @property
4317760c2415Smrg {
4318760c2415Smrg string code = "";
4319760c2415Smrg
4320760c2415Smrg foreach (i_; CountUp!(0 + oset.contents.length)) // workaround
4321760c2415Smrg {
4322760c2415Smrg enum i = 0 + i_; // workaround
4323760c2415Smrg code ~= generateFunction!(
4324760c2415Smrg Policy.FUNCINFO_ID!(oset.name, i), oset.name,
4325760c2415Smrg oset.contents[i]) ~ "\n";
4326760c2415Smrg }
4327760c2415Smrg return code;
4328760c2415Smrg }
4329760c2415Smrg
4330760c2415Smrg /*
4331760c2415Smrg * Returns D code which implements the function func. This function
4332760c2415Smrg * actually generates only the declarator part; the function body part is
4333760c2415Smrg * generated by the functionGenerator() policy.
4334760c2415Smrg */
4335760c2415Smrg public string generateFunction(
4336760c2415Smrg string myFuncInfo, string name, func... )() @property
4337760c2415Smrg {
4338760c2415Smrg import std.format : format;
4339760c2415Smrg
4340760c2415Smrg enum isCtor = (name == CONSTRUCTOR_NAME);
4341760c2415Smrg
4342760c2415Smrg string code; // the result
4343760c2415Smrg
4344760c2415Smrg auto paramsRes = generateParameters!(myFuncInfo, func)();
4345760c2415Smrg code ~= paramsRes.imports;
4346760c2415Smrg
4347760c2415Smrg /*** Function Declarator ***/
4348760c2415Smrg {
4349760c2415Smrg alias Func = FunctionTypeOf!(func);
4350760c2415Smrg alias FA = FunctionAttribute;
4351760c2415Smrg enum atts = functionAttributes!(func);
4352760c2415Smrg enum realName = isCtor ? "this" : name;
4353760c2415Smrg
4354760c2415Smrg // FIXME?? Make it so that these aren't CTFE funcs any more, since
4355760c2415Smrg // Format is deprecated, and format works at compile time?
4356760c2415Smrg /* Made them CTFE funcs just for the sake of Format!(...) */
4357760c2415Smrg
4358760c2415Smrg // return type with optional "ref"
4359760c2415Smrg static string make_returnType()
4360760c2415Smrg {
4361760c2415Smrg string rtype = "";
4362760c2415Smrg
4363760c2415Smrg if (!isCtor)
4364760c2415Smrg {
4365760c2415Smrg if (atts & FA.ref_) rtype ~= "ref ";
4366760c2415Smrg rtype ~= myFuncInfo ~ ".RT";
4367760c2415Smrg }
4368760c2415Smrg return rtype;
4369760c2415Smrg }
4370760c2415Smrg enum returnType = make_returnType();
4371760c2415Smrg
4372760c2415Smrg // function attributes attached after declaration
4373760c2415Smrg static string make_postAtts()
4374760c2415Smrg {
4375760c2415Smrg string poatts = "";
4376760c2415Smrg if (atts & FA.pure_ ) poatts ~= " pure";
4377760c2415Smrg if (atts & FA.nothrow_) poatts ~= " nothrow";
4378760c2415Smrg if (atts & FA.property) poatts ~= " @property";
4379760c2415Smrg if (atts & FA.safe ) poatts ~= " @safe";
4380760c2415Smrg if (atts & FA.trusted ) poatts ~= " @trusted";
4381760c2415Smrg return poatts;
4382760c2415Smrg }
4383760c2415Smrg enum postAtts = make_postAtts();
4384760c2415Smrg
4385760c2415Smrg // function storage class
4386760c2415Smrg static string make_storageClass()
4387760c2415Smrg {
4388760c2415Smrg string postc = "";
4389760c2415Smrg if (is(Func == shared)) postc ~= " shared";
4390760c2415Smrg if (is(Func == const)) postc ~= " const";
4391760c2415Smrg if (is(Func == inout)) postc ~= " inout";
4392760c2415Smrg if (is(Func == immutable)) postc ~= " immutable";
4393760c2415Smrg return postc;
4394760c2415Smrg }
4395760c2415Smrg enum storageClass = make_storageClass();
4396760c2415Smrg
4397760c2415Smrg //
4398760c2415Smrg if (__traits(isVirtualMethod, func))
4399760c2415Smrg code ~= "override ";
4400760c2415Smrg code ~= format("extern(%s) %s %s(%s) %s %s\n",
4401760c2415Smrg functionLinkage!(func),
4402760c2415Smrg returnType,
4403760c2415Smrg realName,
4404760c2415Smrg paramsRes.params,
4405760c2415Smrg postAtts, storageClass );
4406760c2415Smrg }
4407760c2415Smrg
4408760c2415Smrg /*** Function Body ***/
4409760c2415Smrg code ~= "{\n";
4410760c2415Smrg {
4411760c2415Smrg enum nparams = Parameters!(func).length;
4412760c2415Smrg
4413760c2415Smrg /* Declare keywords: args, self and parent. */
4414760c2415Smrg string preamble;
4415760c2415Smrg
4416760c2415Smrg preamble ~= "alias args = AliasSeq!(" ~ enumerateParameters!(nparams) ~ ");\n";
4417760c2415Smrg if (!isCtor)
4418760c2415Smrg {
4419760c2415Smrg preamble ~= "alias self = " ~ name ~ ";\n";
4420760c2415Smrg if (WITH_BASE_CLASS && !__traits(isAbstractFunction, func))
4421760c2415Smrg preamble ~= "alias parent = AliasSeq!(__traits(getMember, super, \"" ~ name ~ "\"))[0];";
4422760c2415Smrg }
4423760c2415Smrg
4424760c2415Smrg // Function body
4425760c2415Smrg static if (WITHOUT_SYMBOL)
4426760c2415Smrg enum fbody = Policy.generateFunctionBody!(name, func);
4427760c2415Smrg else
4428760c2415Smrg enum fbody = Policy.generateFunctionBody!(func);
4429760c2415Smrg
4430760c2415Smrg code ~= preamble;
4431760c2415Smrg code ~= fbody;
4432760c2415Smrg }
4433760c2415Smrg code ~= "}";
4434760c2415Smrg
4435760c2415Smrg return code;
4436760c2415Smrg }
4437760c2415Smrg
4438760c2415Smrg /*
4439760c2415Smrg * Returns D code which declares function parameters,
4440760c2415Smrg * and optionally any imports (e.g. core.vararg)
4441760c2415Smrg * "ref int a0, real a1, ..."
4442760c2415Smrg */
4443760c2415Smrg static struct GenParams { string imports, params; }
4444760c2415Smrg private GenParams generateParameters(string myFuncInfo, func...)()
4445760c2415Smrg {
4446760c2415Smrg alias STC = ParameterStorageClass;
4447760c2415Smrg alias stcs = ParameterStorageClassTuple!(func);
4448760c2415Smrg enum nparams = stcs.length;
4449760c2415Smrg
4450760c2415Smrg string imports = ""; // any imports required
4451760c2415Smrg string params = ""; // parameters
4452760c2415Smrg
4453760c2415Smrg foreach (i, stc; stcs)
4454760c2415Smrg {
4455760c2415Smrg if (i > 0) params ~= ", ";
4456760c2415Smrg
4457760c2415Smrg // Parameter storage classes.
4458760c2415Smrg if (stc & STC.scope_) params ~= "scope ";
4459760c2415Smrg if (stc & STC.out_ ) params ~= "out ";
4460760c2415Smrg if (stc & STC.ref_ ) params ~= "ref ";
4461760c2415Smrg if (stc & STC.lazy_ ) params ~= "lazy ";
4462760c2415Smrg
4463760c2415Smrg // Take parameter type from the FuncInfo.
4464760c2415Smrg params ~= format("%s.PT[%s]", myFuncInfo, i);
4465760c2415Smrg
4466760c2415Smrg // Declare a parameter variable.
4467760c2415Smrg params ~= " " ~ PARAMETER_VARIABLE_ID!(i);
4468760c2415Smrg }
4469760c2415Smrg
4470760c2415Smrg // Add some ellipsis part if needed.
4471760c2415Smrg auto style = variadicFunctionStyle!(func);
4472760c2415Smrg final switch (style)
4473760c2415Smrg {
4474760c2415Smrg case Variadic.no:
4475760c2415Smrg break;
4476760c2415Smrg
4477760c2415Smrg case Variadic.c, Variadic.d:
4478760c2415Smrg imports ~= "import core.vararg;\n";
4479760c2415Smrg // (...) or (a, b, ...)
4480760c2415Smrg params ~= (nparams == 0) ? "..." : ", ...";
4481760c2415Smrg break;
4482760c2415Smrg
4483760c2415Smrg case Variadic.typesafe:
4484760c2415Smrg params ~= " ...";
4485760c2415Smrg break;
4486760c2415Smrg }
4487760c2415Smrg
4488760c2415Smrg return typeof(return)(imports, params);
4489760c2415Smrg }
4490760c2415Smrg
4491760c2415Smrg // Returns D code which enumerates n parameter variables using comma as the
4492760c2415Smrg // separator. "a0, a1, a2, a3"
4493760c2415Smrg private string enumerateParameters(size_t n)() @property
4494760c2415Smrg {
4495760c2415Smrg string params = "";
4496760c2415Smrg
4497760c2415Smrg foreach (i_; CountUp!(n))
4498760c2415Smrg {
4499760c2415Smrg enum i = 0 + i_; // workaround
4500760c2415Smrg if (i > 0) params ~= ", ";
4501760c2415Smrg params ~= PARAMETER_VARIABLE_ID!(i);
4502760c2415Smrg }
4503760c2415Smrg return params;
4504760c2415Smrg }
4505760c2415Smrg }
4506760c2415Smrg
4507760c2415Smrg
4508760c2415Smrg /**
4509760c2415Smrg Predefined how-policies for $(D AutoImplement). These templates are also used by
4510760c2415Smrg $(D BlackHole) and $(D WhiteHole), respectively.
4511760c2415Smrg */
4512760c2415Smrg template generateEmptyFunction(C, func.../+[BUG 4217]+/)
4513760c2415Smrg {
4514760c2415Smrg static if (is(ReturnType!(func) == void))
4515760c2415Smrg enum string generateEmptyFunction = q{
4516760c2415Smrg };
4517760c2415Smrg else static if (functionAttributes!(func) & FunctionAttribute.ref_)
4518760c2415Smrg enum string generateEmptyFunction = q{
4519760c2415Smrg static typeof(return) dummy;
4520760c2415Smrg return dummy;
4521760c2415Smrg };
4522760c2415Smrg else
4523760c2415Smrg enum string generateEmptyFunction = q{
4524760c2415Smrg return typeof(return).init;
4525760c2415Smrg };
4526760c2415Smrg }
4527760c2415Smrg
4528760c2415Smrg /// ditto
4529760c2415Smrg template generateAssertTrap(C, func...)
4530760c2415Smrg {
4531760c2415Smrg enum string generateAssertTrap =
4532760c2415Smrg `throw new NotImplementedError("` ~ C.stringof ~ "."
4533760c2415Smrg ~ __traits(identifier, func) ~ `");`;
4534760c2415Smrg }
4535760c2415Smrg
4536760c2415Smrg private
4537760c2415Smrg {
4538760c2415Smrg pragma(mangle, "_d_toObject")
4539760c2415Smrg extern(C) pure nothrow Object typecons_d_toObject(void* p);
4540760c2415Smrg }
4541760c2415Smrg
4542760c2415Smrg /*
4543760c2415Smrg * Avoids opCast operator overloading.
4544760c2415Smrg */
4545760c2415Smrg private template dynamicCast(T)
4546760c2415Smrg if (is(T == class) || is(T == interface))
4547760c2415Smrg {
4548760c2415Smrg @trusted
4549760c2415Smrg T dynamicCast(S)(inout S source)
4550760c2415Smrg if (is(S == class) || is(S == interface))
4551760c2415Smrg {
4552760c2415Smrg static if (is(Unqual!S : Unqual!T))
4553760c2415Smrg {
4554760c2415Smrg import std.traits : QualifierOf;
4555760c2415Smrg alias Qual = QualifierOf!S; // SharedOf or MutableOf
4556760c2415Smrg alias TmpT = Qual!(Unqual!T);
4557760c2415Smrg inout(TmpT) tmp = source; // bypass opCast by implicit conversion
4558760c2415Smrg return *cast(T*)(&tmp); // + variable pointer cast + dereference
4559760c2415Smrg }
4560760c2415Smrg else
4561760c2415Smrg {
4562760c2415Smrg return cast(T) typecons_d_toObject(*cast(void**)(&source));
4563760c2415Smrg }
4564760c2415Smrg }
4565760c2415Smrg }
4566760c2415Smrg
4567760c2415Smrg @system unittest
4568760c2415Smrg {
4569760c2415Smrg class C { @disable opCast(T)() {} }
4570760c2415Smrg auto c = new C;
4571760c2415Smrg static assert(!__traits(compiles, cast(Object) c));
4572760c2415Smrg auto o = dynamicCast!Object(c);
4573760c2415Smrg assert(c is o);
4574760c2415Smrg
4575760c2415Smrg interface I { @disable opCast(T)() {} Object instance(); }
4576760c2415Smrg interface J { @disable opCast(T)() {} Object instance(); }
4577760c2415Smrg class D : I, J { Object instance() { return this; } }
4578760c2415Smrg I i = new D();
4579760c2415Smrg static assert(!__traits(compiles, cast(J) i));
4580760c2415Smrg J j = dynamicCast!J(i);
4581760c2415Smrg assert(i.instance() is j.instance());
4582760c2415Smrg }
4583760c2415Smrg
4584760c2415Smrg /**
4585760c2415Smrg * Supports structural based typesafe conversion.
4586760c2415Smrg *
4587760c2415Smrg * If $(D Source) has structural conformance with the $(D interface) $(D Targets),
4588760c2415Smrg * wrap creates internal wrapper class which inherits $(D Targets) and
4589760c2415Smrg * wrap $(D src) object, then return it.
4590760c2415Smrg */
4591760c2415Smrg template wrap(Targets...)
4592760c2415Smrg if (Targets.length >= 1 && allSatisfy!(isMutable, Targets))
4593760c2415Smrg {
4594760c2415Smrg import std.meta : staticMap;
4595760c2415Smrg
4596760c2415Smrg // strict upcast
4597760c2415Smrg auto wrap(Source)(inout Source src) @trusted pure nothrow
4598760c2415Smrg if (Targets.length == 1 && is(Source : Targets[0]))
4599760c2415Smrg {
4600760c2415Smrg alias T = Select!(is(Source == shared), shared Targets[0], Targets[0]);
4601760c2415Smrg return dynamicCast!(inout T)(src);
4602760c2415Smrg }
4603760c2415Smrg // structural upcast
4604760c2415Smrg template wrap(Source)
4605760c2415Smrg if (!allSatisfy!(Bind!(isImplicitlyConvertible, Source), Targets))
4606760c2415Smrg {
4607760c2415Smrg auto wrap(inout Source src)
4608760c2415Smrg {
4609760c2415Smrg static assert(hasRequireMethods!(),
4610760c2415Smrg "Source "~Source.stringof~
4611760c2415Smrg " does not have structural conformance to "~
4612760c2415Smrg Targets.stringof);
4613760c2415Smrg
4614760c2415Smrg alias T = Select!(is(Source == shared), shared Impl, Impl);
4615760c2415Smrg return new inout T(src);
4616760c2415Smrg }
4617760c2415Smrg
4618760c2415Smrg template FuncInfo(string s, F)
4619760c2415Smrg {
4620760c2415Smrg enum name = s;
4621760c2415Smrg alias type = F;
4622760c2415Smrg }
4623760c2415Smrg
4624760c2415Smrg // Concat all Targets function members into one tuple
4625760c2415Smrg template Concat(size_t i = 0)
4626760c2415Smrg {
4627760c2415Smrg static if (i >= Targets.length)
4628760c2415Smrg alias Concat = AliasSeq!();
4629760c2415Smrg else
4630760c2415Smrg {
4631760c2415Smrg alias Concat = AliasSeq!(GetOverloadedMethods!(Targets[i]), Concat!(i + 1));
4632760c2415Smrg }
4633760c2415Smrg }
4634760c2415Smrg // Remove duplicated functions based on the identifier name and function type covariance
4635760c2415Smrg template Uniq(members...)
4636760c2415Smrg {
4637760c2415Smrg static if (members.length == 0)
4638760c2415Smrg alias Uniq = AliasSeq!();
4639760c2415Smrg else
4640760c2415Smrg {
4641760c2415Smrg alias func = members[0];
4642760c2415Smrg enum name = __traits(identifier, func);
4643760c2415Smrg alias type = FunctionTypeOf!func;
4644760c2415Smrg template check(size_t i, mem...)
4645760c2415Smrg {
4646760c2415Smrg static if (i >= mem.length)
4647760c2415Smrg enum ptrdiff_t check = -1;
4648760c2415Smrg else
4649760c2415Smrg {
4650760c2415Smrg enum ptrdiff_t check =
4651760c2415Smrg __traits(identifier, func) == __traits(identifier, mem[i]) &&
4652760c2415Smrg !is(DerivedFunctionType!(type, FunctionTypeOf!(mem[i])) == void)
4653760c2415Smrg ? i : check!(i + 1, mem);
4654760c2415Smrg }
4655760c2415Smrg }
4656760c2415Smrg enum ptrdiff_t x = 1 + check!(0, members[1 .. $]);
4657760c2415Smrg static if (x >= 1)
4658760c2415Smrg {
4659760c2415Smrg alias typex = DerivedFunctionType!(type, FunctionTypeOf!(members[x]));
4660760c2415Smrg alias remain = Uniq!(members[1 .. x], members[x + 1 .. $]);
4661760c2415Smrg
4662760c2415Smrg static if (remain.length >= 1 && remain[0].name == name &&
4663760c2415Smrg !is(DerivedFunctionType!(typex, remain[0].type) == void))
4664760c2415Smrg {
4665760c2415Smrg alias F = DerivedFunctionType!(typex, remain[0].type);
4666760c2415Smrg alias Uniq = AliasSeq!(FuncInfo!(name, F), remain[1 .. $]);
4667760c2415Smrg }
4668760c2415Smrg else
4669760c2415Smrg alias Uniq = AliasSeq!(FuncInfo!(name, typex), remain);
4670760c2415Smrg }
4671760c2415Smrg else
4672760c2415Smrg {
4673760c2415Smrg alias Uniq = AliasSeq!(FuncInfo!(name, type), Uniq!(members[1 .. $]));
4674760c2415Smrg }
4675760c2415Smrg }
4676760c2415Smrg }
4677760c2415Smrg alias TargetMembers = Uniq!(Concat!()); // list of FuncInfo
4678760c2415Smrg alias SourceMembers = GetOverloadedMethods!Source; // list of function symbols
4679760c2415Smrg
4680760c2415Smrg // Check whether all of SourceMembers satisfy covariance target in TargetMembers
4681760c2415Smrg template hasRequireMethods(size_t i = 0)
4682760c2415Smrg {
4683760c2415Smrg static if (i >= TargetMembers.length)
4684760c2415Smrg enum hasRequireMethods = true;
4685760c2415Smrg else
4686760c2415Smrg {
4687760c2415Smrg enum hasRequireMethods =
4688760c2415Smrg findCovariantFunction!(TargetMembers[i], Source, SourceMembers) != -1 &&
4689760c2415Smrg hasRequireMethods!(i + 1);
4690760c2415Smrg }
4691760c2415Smrg }
4692760c2415Smrg
4693760c2415Smrg // Internal wrapper class
4694760c2415Smrg final class Impl : Structural, Targets
4695760c2415Smrg {
4696760c2415Smrg private:
4697760c2415Smrg Source _wrap_source;
4698760c2415Smrg
4699760c2415Smrg this( inout Source s) inout @safe pure nothrow { _wrap_source = s; }
4700760c2415Smrg this(shared inout Source s) shared inout @safe pure nothrow { _wrap_source = s; }
4701760c2415Smrg
4702760c2415Smrg // BUG: making private should work with NVI.
4703760c2415Smrg protected final inout(Object) _wrap_getSource() inout @trusted
4704760c2415Smrg {
4705760c2415Smrg return dynamicCast!(inout Object)(_wrap_source);
4706760c2415Smrg }
4707760c2415Smrg
4708760c2415Smrg import std.conv : to;
4709760c2415Smrg import std.functional : forward;
4710760c2415Smrg template generateFun(size_t i)
4711760c2415Smrg {
4712760c2415Smrg enum name = TargetMembers[i].name;
4713760c2415Smrg enum fa = functionAttributes!(TargetMembers[i].type);
4714760c2415Smrg static @property stc()
4715760c2415Smrg {
4716760c2415Smrg string r;
4717760c2415Smrg if (fa & FunctionAttribute.property) r ~= "@property ";
4718760c2415Smrg if (fa & FunctionAttribute.ref_) r ~= "ref ";
4719760c2415Smrg if (fa & FunctionAttribute.pure_) r ~= "pure ";
4720760c2415Smrg if (fa & FunctionAttribute.nothrow_) r ~= "nothrow ";
4721760c2415Smrg if (fa & FunctionAttribute.trusted) r ~= "@trusted ";
4722760c2415Smrg if (fa & FunctionAttribute.safe) r ~= "@safe ";
4723760c2415Smrg return r;
4724760c2415Smrg }
4725760c2415Smrg static @property mod()
4726760c2415Smrg {
4727760c2415Smrg alias type = AliasSeq!(TargetMembers[i].type)[0];
4728760c2415Smrg string r;
4729760c2415Smrg static if (is(type == immutable)) r ~= " immutable";
4730760c2415Smrg else
4731760c2415Smrg {
4732760c2415Smrg static if (is(type == shared)) r ~= " shared";
4733760c2415Smrg static if (is(type == const)) r ~= " const";
4734760c2415Smrg else static if (is(type == inout)) r ~= " inout";
4735760c2415Smrg //else --> mutable
4736760c2415Smrg }
4737760c2415Smrg return r;
4738760c2415Smrg }
4739760c2415Smrg enum n = to!string(i);
4740760c2415Smrg static if (fa & FunctionAttribute.property)
4741760c2415Smrg {
4742760c2415Smrg static if (Parameters!(TargetMembers[i].type).length == 0)
4743760c2415Smrg enum fbody = "_wrap_source."~name;
4744760c2415Smrg else
4745760c2415Smrg enum fbody = "_wrap_source."~name~" = forward!args";
4746760c2415Smrg }
4747760c2415Smrg else
4748760c2415Smrg {
4749760c2415Smrg enum fbody = "_wrap_source."~name~"(forward!args)";
4750760c2415Smrg }
4751760c2415Smrg enum generateFun =
4752760c2415Smrg "override "~stc~"ReturnType!(TargetMembers["~n~"].type) "
4753760c2415Smrg ~ name~"(Parameters!(TargetMembers["~n~"].type) args) "~mod~
4754760c2415Smrg "{ return "~fbody~"; }";
4755760c2415Smrg }
4756760c2415Smrg
4757760c2415Smrg public:
4758760c2415Smrg mixin mixinAll!(
4759760c2415Smrg staticMap!(generateFun, staticIota!(0, TargetMembers.length)));
4760760c2415Smrg }
4761760c2415Smrg }
4762760c2415Smrg }
4763760c2415Smrg /// ditto
4764760c2415Smrg template wrap(Targets...)
4765760c2415Smrg if (Targets.length >= 1 && !allSatisfy!(isMutable, Targets))
4766760c2415Smrg {
4767760c2415Smrg import std.meta : staticMap;
4768760c2415Smrg
4769760c2415Smrg alias wrap = .wrap!(staticMap!(Unqual, Targets));
4770760c2415Smrg }
4771760c2415Smrg
4772760c2415Smrg // Internal class to support dynamic cross-casting
4773760c2415Smrg private interface Structural
4774760c2415Smrg {
4775760c2415Smrg inout(Object) _wrap_getSource() inout @safe pure nothrow;
4776760c2415Smrg }
4777760c2415Smrg
4778760c2415Smrg /**
4779760c2415Smrg * Extract object which wrapped by $(D wrap).
4780760c2415Smrg */
4781760c2415Smrg template unwrap(Target)
4782760c2415Smrg if (isMutable!Target)
4783760c2415Smrg {
4784760c2415Smrg // strict downcast
4785760c2415Smrg auto unwrap(Source)(inout Source src) @trusted pure nothrow
4786760c2415Smrg if (is(Target : Source))
4787760c2415Smrg {
4788760c2415Smrg alias T = Select!(is(Source == shared), shared Target, Target);
4789760c2415Smrg return dynamicCast!(inout T)(src);
4790760c2415Smrg }
4791760c2415Smrg // structural downcast
4792760c2415Smrg auto unwrap(Source)(inout Source src) @trusted pure nothrow
4793760c2415Smrg if (!is(Target : Source))
4794760c2415Smrg {
4795760c2415Smrg alias T = Select!(is(Source == shared), shared Target, Target);
4796760c2415Smrg Object o = dynamicCast!(Object)(src); // remove qualifier
4797760c2415Smrg do
4798760c2415Smrg {
4799760c2415Smrg if (auto a = dynamicCast!(Structural)(o))
4800760c2415Smrg {
4801760c2415Smrg if (auto d = dynamicCast!(inout T)(o = a._wrap_getSource()))
4802760c2415Smrg return d;
4803760c2415Smrg }
4804760c2415Smrg else if (auto d = dynamicCast!(inout T)(o))
4805760c2415Smrg return d;
4806760c2415Smrg else
4807760c2415Smrg break;
4808760c2415Smrg } while (o);
4809760c2415Smrg return null;
4810760c2415Smrg }
4811760c2415Smrg }
4812760c2415Smrg /// ditto
4813760c2415Smrg template unwrap(Target)
4814760c2415Smrg if (!isMutable!Target)
4815760c2415Smrg {
4816760c2415Smrg alias unwrap = .unwrap!(Unqual!Target);
4817760c2415Smrg }
4818760c2415Smrg
4819760c2415Smrg ///
4820760c2415Smrg @system unittest
4821760c2415Smrg {
4822760c2415Smrg interface Quack
4823760c2415Smrg {
4824760c2415Smrg int quack();
4825760c2415Smrg @property int height();
4826760c2415Smrg }
4827760c2415Smrg interface Flyer
4828760c2415Smrg {
4829760c2415Smrg @property int height();
4830760c2415Smrg }
4831760c2415Smrg class Duck : Quack
4832760c2415Smrg {
4833760c2415Smrg int quack() { return 1; }
4834760c2415Smrg @property int height() { return 10; }
4835760c2415Smrg }
4836760c2415Smrg class Human
4837760c2415Smrg {
4838760c2415Smrg int quack() { return 2; }
4839760c2415Smrg @property int height() { return 20; }
4840760c2415Smrg }
4841760c2415Smrg
4842760c2415Smrg Duck d1 = new Duck();
4843760c2415Smrg Human h1 = new Human();
4844760c2415Smrg
4845760c2415Smrg interface Refleshable
4846760c2415Smrg {
4847760c2415Smrg int reflesh();
4848760c2415Smrg }
4849760c2415Smrg // does not have structural conformance
4850760c2415Smrg static assert(!__traits(compiles, d1.wrap!Refleshable));
4851760c2415Smrg static assert(!__traits(compiles, h1.wrap!Refleshable));
4852760c2415Smrg
4853760c2415Smrg // strict upcast
4854760c2415Smrg Quack qd = d1.wrap!Quack;
4855760c2415Smrg assert(qd is d1);
4856760c2415Smrg assert(qd.quack() == 1); // calls Duck.quack
4857760c2415Smrg // strict downcast
4858760c2415Smrg Duck d2 = qd.unwrap!Duck;
4859760c2415Smrg assert(d2 is d1);
4860760c2415Smrg
4861760c2415Smrg // structural upcast
4862760c2415Smrg Quack qh = h1.wrap!Quack;
4863760c2415Smrg assert(qh.quack() == 2); // calls Human.quack
4864760c2415Smrg // structural downcast
4865760c2415Smrg Human h2 = qh.unwrap!Human;
4866760c2415Smrg assert(h2 is h1);
4867760c2415Smrg
4868760c2415Smrg // structural upcast (two steps)
4869760c2415Smrg Quack qx = h1.wrap!Quack; // Human -> Quack
4870760c2415Smrg Flyer fx = qx.wrap!Flyer; // Quack -> Flyer
4871760c2415Smrg assert(fx.height == 20); // calls Human.height
4872760c2415Smrg // strucural downcast (two steps)
4873760c2415Smrg Quack qy = fx.unwrap!Quack; // Flyer -> Quack
4874760c2415Smrg Human hy = qy.unwrap!Human; // Quack -> Human
4875760c2415Smrg assert(hy is h1);
4876760c2415Smrg // strucural downcast (one step)
4877760c2415Smrg Human hz = fx.unwrap!Human; // Flyer -> Human
4878760c2415Smrg assert(hz is h1);
4879760c2415Smrg }
4880760c2415Smrg ///
4881760c2415Smrg @system unittest
4882760c2415Smrg {
4883760c2415Smrg import std.traits : FunctionAttribute, functionAttributes;
4884760c2415Smrg interface A { int run(); }
4885760c2415Smrg interface B { int stop(); @property int status(); }
4886760c2415Smrg class X
4887760c2415Smrg {
4888760c2415Smrg int run() { return 1; }
4889760c2415Smrg int stop() { return 2; }
4890760c2415Smrg @property int status() { return 3; }
4891760c2415Smrg }
4892760c2415Smrg
4893760c2415Smrg auto x = new X();
4894760c2415Smrg auto ab = x.wrap!(A, B);
4895760c2415Smrg A a = ab;
4896760c2415Smrg B b = ab;
4897760c2415Smrg assert(a.run() == 1);
4898760c2415Smrg assert(b.stop() == 2);
4899760c2415Smrg assert(b.status == 3);
4900760c2415Smrg static assert(functionAttributes!(typeof(ab).status) & FunctionAttribute.property);
4901760c2415Smrg }
4902760c2415Smrg @system unittest
4903760c2415Smrg {
4904760c2415Smrg class A
4905760c2415Smrg {
4906760c2415Smrg int draw() { return 1; }
4907760c2415Smrg int draw(int v) { return v; }
4908760c2415Smrg
4909760c2415Smrg int draw() const { return 2; }
4910760c2415Smrg int draw() shared { return 3; }
4911760c2415Smrg int draw() shared const { return 4; }
4912760c2415Smrg int draw() immutable { return 5; }
4913760c2415Smrg }
4914760c2415Smrg interface Drawable
4915760c2415Smrg {
4916760c2415Smrg int draw();
4917760c2415Smrg int draw() const;
4918760c2415Smrg int draw() shared;
4919760c2415Smrg int draw() shared const;
4920760c2415Smrg int draw() immutable;
4921760c2415Smrg }
4922760c2415Smrg interface Drawable2
4923760c2415Smrg {
4924760c2415Smrg int draw(int v);
4925760c2415Smrg }
4926760c2415Smrg
4927760c2415Smrg auto ma = new A();
4928760c2415Smrg auto sa = new shared A();
4929760c2415Smrg auto ia = new immutable A();
4930760c2415Smrg {
4931760c2415Smrg Drawable md = ma.wrap!Drawable;
4932760c2415Smrg const Drawable cd = ma.wrap!Drawable;
4933760c2415Smrg shared Drawable sd = sa.wrap!Drawable;
4934760c2415Smrg shared const Drawable scd = sa.wrap!Drawable;
4935760c2415Smrg immutable Drawable id = ia.wrap!Drawable;
4936760c2415Smrg assert( md.draw() == 1);
4937760c2415Smrg assert( cd.draw() == 2);
4938760c2415Smrg assert( sd.draw() == 3);
4939760c2415Smrg assert(scd.draw() == 4);
4940760c2415Smrg assert( id.draw() == 5);
4941760c2415Smrg }
4942760c2415Smrg {
4943760c2415Smrg Drawable2 d = ma.wrap!Drawable2;
4944760c2415Smrg static assert(!__traits(compiles, d.draw()));
4945760c2415Smrg assert(d.draw(10) == 10);
4946760c2415Smrg }
4947760c2415Smrg }
4948760c2415Smrg @system unittest
4949760c2415Smrg {
4950760c2415Smrg // Bugzilla 10377
4951760c2415Smrg import std.range, std.algorithm;
4952760c2415Smrg
4953760c2415Smrg interface MyInputRange(T)
4954760c2415Smrg {
4955760c2415Smrg @property T front();
4956760c2415Smrg void popFront();
4957760c2415Smrg @property bool empty();
4958760c2415Smrg }
4959760c2415Smrg
4960760c2415Smrg //auto o = iota(0,10,1).inputRangeObject();
4961760c2415Smrg //pragma(msg, __traits(allMembers, typeof(o)));
4962760c2415Smrg auto r = iota(0,10,1).inputRangeObject().wrap!(MyInputRange!int)();
4963760c2415Smrg assert(equal(r, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]));
4964760c2415Smrg }
4965760c2415Smrg @system unittest
4966760c2415Smrg {
4967760c2415Smrg // Bugzilla 10536
4968760c2415Smrg interface Interface
4969760c2415Smrg {
4970760c2415Smrg int foo();
4971760c2415Smrg }
4972760c2415Smrg class Pluggable
4973760c2415Smrg {
4974760c2415Smrg int foo() { return 1; }
4975760c2415Smrg @disable void opCast(T, this X)(); // !
4976760c2415Smrg }
4977760c2415Smrg
4978760c2415Smrg Interface i = new Pluggable().wrap!Interface;
4979760c2415Smrg assert(i.foo() == 1);
4980760c2415Smrg }
4981760c2415Smrg @system unittest
4982760c2415Smrg {
4983760c2415Smrg // Enhancement 10538
4984760c2415Smrg interface Interface
4985760c2415Smrg {
4986760c2415Smrg int foo();
4987760c2415Smrg int bar(int);
4988760c2415Smrg }
4989760c2415Smrg class Pluggable
4990760c2415Smrg {
4991760c2415Smrg int opDispatch(string name, A...)(A args) { return 100; }
4992760c2415Smrg }
4993760c2415Smrg
4994760c2415Smrg Interface i = wrap!Interface(new Pluggable());
4995760c2415Smrg assert(i.foo() == 100);
4996760c2415Smrg assert(i.bar(10) == 100);
4997760c2415Smrg }
4998760c2415Smrg
4999760c2415Smrg // Make a tuple of non-static function symbols
5000760c2415Smrg package template GetOverloadedMethods(T)
5001760c2415Smrg {
5002760c2415Smrg import std.meta : Filter;
5003760c2415Smrg
5004760c2415Smrg alias allMembers = AliasSeq!(__traits(allMembers, T));
5005760c2415Smrg template follows(size_t i = 0)
5006760c2415Smrg {
5007760c2415Smrg static if (i >= allMembers.length)
5008760c2415Smrg {
5009760c2415Smrg alias follows = AliasSeq!();
5010760c2415Smrg }
5011760c2415Smrg else static if (!__traits(compiles, mixin("T."~allMembers[i])))
5012760c2415Smrg {
5013760c2415Smrg alias follows = follows!(i + 1);
5014760c2415Smrg }
5015760c2415Smrg else
5016760c2415Smrg {
5017760c2415Smrg enum name = allMembers[i];
5018760c2415Smrg
5019760c2415Smrg template isMethod(alias f)
5020760c2415Smrg {
5021760c2415Smrg static if (is(typeof(&f) F == F*) && is(F == function))
5022760c2415Smrg enum isMethod = !__traits(isStaticFunction, f);
5023760c2415Smrg else
5024760c2415Smrg enum isMethod = false;
5025760c2415Smrg }
5026760c2415Smrg alias follows = AliasSeq!(
5027760c2415Smrg std.meta.Filter!(isMethod, __traits(getOverloads, T, name)),
5028760c2415Smrg follows!(i + 1));
5029760c2415Smrg }
5030760c2415Smrg }
5031760c2415Smrg alias GetOverloadedMethods = follows!();
5032760c2415Smrg }
5033760c2415Smrg // find a function from Fs that has same identifier and covariant type with f
5034760c2415Smrg private template findCovariantFunction(alias finfo, Source, Fs...)
5035760c2415Smrg {
5036760c2415Smrg template check(size_t i = 0)
5037760c2415Smrg {
5038760c2415Smrg static if (i >= Fs.length)
5039760c2415Smrg enum ptrdiff_t check = -1;
5040760c2415Smrg else
5041760c2415Smrg {
5042760c2415Smrg enum ptrdiff_t check =
5043760c2415Smrg (finfo.name == __traits(identifier, Fs[i])) &&
5044760c2415Smrg isCovariantWith!(FunctionTypeOf!(Fs[i]), finfo.type)
5045760c2415Smrg ? i : check!(i + 1);
5046760c2415Smrg }
5047760c2415Smrg }
5048760c2415Smrg enum x = check!();
5049760c2415Smrg static if (x == -1 && is(typeof(Source.opDispatch)))
5050760c2415Smrg {
5051760c2415Smrg alias Params = Parameters!(finfo.type);
5052760c2415Smrg enum ptrdiff_t findCovariantFunction =
5053760c2415Smrg is(typeof(( Source).init.opDispatch!(finfo.name)(Params.init))) ||
5054760c2415Smrg is(typeof(( const Source).init.opDispatch!(finfo.name)(Params.init))) ||
5055760c2415Smrg is(typeof(( immutable Source).init.opDispatch!(finfo.name)(Params.init))) ||
5056760c2415Smrg is(typeof(( shared Source).init.opDispatch!(finfo.name)(Params.init))) ||
5057760c2415Smrg is(typeof((shared const Source).init.opDispatch!(finfo.name)(Params.init)))
5058760c2415Smrg ? ptrdiff_t.max : -1;
5059760c2415Smrg }
5060760c2415Smrg else
5061760c2415Smrg enum ptrdiff_t findCovariantFunction = x;
5062760c2415Smrg }
5063760c2415Smrg
5064760c2415Smrg private enum TypeModifier
5065760c2415Smrg {
5066760c2415Smrg mutable = 0, // type is mutable
5067760c2415Smrg const_ = 1, // type is const
5068760c2415Smrg immutable_ = 2, // type is immutable
5069760c2415Smrg shared_ = 4, // type is shared
5070760c2415Smrg inout_ = 8, // type is wild
5071760c2415Smrg }
5072760c2415Smrg private template TypeMod(T)
5073760c2415Smrg {
5074760c2415Smrg static if (is(T == immutable))
5075760c2415Smrg {
5076760c2415Smrg enum mod1 = TypeModifier.immutable_;
5077760c2415Smrg enum mod2 = 0;
5078760c2415Smrg }
5079760c2415Smrg else
5080760c2415Smrg {
5081760c2415Smrg enum mod1 = is(T == shared) ? TypeModifier.shared_ : 0;
5082760c2415Smrg static if (is(T == const))
5083760c2415Smrg enum mod2 = TypeModifier.const_;
5084760c2415Smrg else static if (is(T == inout))
5085760c2415Smrg enum mod2 = TypeModifier.inout_;
5086760c2415Smrg else
5087760c2415Smrg enum mod2 = TypeModifier.mutable;
5088760c2415Smrg }
5089760c2415Smrg enum TypeMod = cast(TypeModifier)(mod1 | mod2);
5090760c2415Smrg }
5091760c2415Smrg
5092760c2415Smrg version (unittest)
5093760c2415Smrg {
5094760c2415Smrg private template UnittestFuncInfo(alias f)
5095760c2415Smrg {
5096760c2415Smrg enum name = __traits(identifier, f);
5097760c2415Smrg alias type = FunctionTypeOf!f;
5098760c2415Smrg }
5099760c2415Smrg }
5100760c2415Smrg @system unittest
5101760c2415Smrg {
5102760c2415Smrg class A
5103760c2415Smrg {
5104760c2415Smrg int draw() { return 1; }
5105760c2415Smrg @property int value() { return 2; }
5106760c2415Smrg final int run() { return 3; }
5107760c2415Smrg }
5108760c2415Smrg alias methods = GetOverloadedMethods!A;
5109760c2415Smrg
5110760c2415Smrg alias int F1();
5111760c2415Smrg alias @property int F2();
5112760c2415Smrg alias string F3();
5113760c2415Smrg alias nothrow @trusted uint F4();
5114760c2415Smrg alias int F5(Object);
5115760c2415Smrg alias bool F6(Object);
5116760c2415Smrg static assert(methods.length == 3 + 4);
5117760c2415Smrg static assert(__traits(identifier, methods[0]) == "draw" && is(typeof(&methods[0]) == F1*));
5118760c2415Smrg static assert(__traits(identifier, methods[1]) == "value" && is(typeof(&methods[1]) == F2*));
5119760c2415Smrg static assert(__traits(identifier, methods[2]) == "run" && is(typeof(&methods[2]) == F1*));
5120760c2415Smrg
5121760c2415Smrg int draw();
5122760c2415Smrg @property int value();
5123760c2415Smrg void opEquals();
5124760c2415Smrg int nomatch();
5125760c2415Smrg static assert(findCovariantFunction!(UnittestFuncInfo!draw, A, methods) == 0);
5126760c2415Smrg static assert(findCovariantFunction!(UnittestFuncInfo!value, A, methods) == 1);
5127760c2415Smrg static assert(findCovariantFunction!(UnittestFuncInfo!opEquals, A, methods) == -1);
5128760c2415Smrg static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, A, methods) == -1);
5129760c2415Smrg
5130760c2415Smrg // considering opDispatch
5131760c2415Smrg class B
5132760c2415Smrg {
5133760c2415Smrg void opDispatch(string name, A...)(A) {}
5134760c2415Smrg }
5135760c2415Smrg alias methodsB = GetOverloadedMethods!B;
5136760c2415Smrg static assert(findCovariantFunction!(UnittestFuncInfo!draw, B, methodsB) == ptrdiff_t.max);
5137760c2415Smrg static assert(findCovariantFunction!(UnittestFuncInfo!value, B, methodsB) == ptrdiff_t.max);
5138760c2415Smrg static assert(findCovariantFunction!(UnittestFuncInfo!opEquals, B, methodsB) == ptrdiff_t.max);
5139760c2415Smrg static assert(findCovariantFunction!(UnittestFuncInfo!nomatch, B, methodsB) == ptrdiff_t.max);
5140760c2415Smrg }
5141760c2415Smrg
5142760c2415Smrg package template DerivedFunctionType(T...)
5143760c2415Smrg {
5144760c2415Smrg static if (!T.length)
5145760c2415Smrg {
5146760c2415Smrg alias DerivedFunctionType = void;
5147760c2415Smrg }
5148760c2415Smrg else static if (T.length == 1)
5149760c2415Smrg {
5150760c2415Smrg static if (is(T[0] == function))
5151760c2415Smrg {
5152760c2415Smrg alias DerivedFunctionType = T[0];
5153760c2415Smrg }
5154760c2415Smrg else
5155760c2415Smrg {
5156760c2415Smrg alias DerivedFunctionType = void;
5157760c2415Smrg }
5158760c2415Smrg }
5159760c2415Smrg else static if (is(T[0] P0 == function) && is(T[1] P1 == function))
5160760c2415Smrg {
5161760c2415Smrg alias FA = FunctionAttribute;
5162760c2415Smrg
5163760c2415Smrg alias F0 = T[0], R0 = ReturnType!F0, PSTC0 = ParameterStorageClassTuple!F0;
5164760c2415Smrg alias F1 = T[1], R1 = ReturnType!F1, PSTC1 = ParameterStorageClassTuple!F1;
5165760c2415Smrg enum FA0 = functionAttributes!F0;
5166760c2415Smrg enum FA1 = functionAttributes!F1;
5167760c2415Smrg
5168760c2415Smrg template CheckParams(size_t i = 0)
5169760c2415Smrg {
5170760c2415Smrg static if (i >= P0.length)
5171760c2415Smrg enum CheckParams = true;
5172760c2415Smrg else
5173760c2415Smrg {
5174760c2415Smrg enum CheckParams = (is(P0[i] == P1[i]) && PSTC0[i] == PSTC1[i]) &&
5175760c2415Smrg CheckParams!(i + 1);
5176760c2415Smrg }
5177760c2415Smrg }
5178760c2415Smrg static if (R0.sizeof == R1.sizeof && !is(CommonType!(R0, R1) == void) &&
5179760c2415Smrg P0.length == P1.length && CheckParams!() && TypeMod!F0 == TypeMod!F1 &&
5180760c2415Smrg variadicFunctionStyle!F0 == variadicFunctionStyle!F1 &&
5181760c2415Smrg functionLinkage!F0 == functionLinkage!F1 &&
5182760c2415Smrg ((FA0 ^ FA1) & (FA.ref_ | FA.property)) == 0)
5183760c2415Smrg {
5184760c2415Smrg alias R = Select!(is(R0 : R1), R0, R1);
5185760c2415Smrg alias FX = FunctionTypeOf!(R function(P0));
5186760c2415Smrg // @system is default
5187760c2415Smrg alias FY = SetFunctionAttributes!(FX, functionLinkage!F0, (FA0 | FA1) & ~FA.system);
5188760c2415Smrg alias DerivedFunctionType = DerivedFunctionType!(FY, T[2 .. $]);
5189760c2415Smrg }
5190760c2415Smrg else
5191760c2415Smrg alias DerivedFunctionType = void;
5192760c2415Smrg }
5193760c2415Smrg else
5194760c2415Smrg alias DerivedFunctionType = void;
5195760c2415Smrg }
5196760c2415Smrg @safe unittest
5197760c2415Smrg {
5198760c2415Smrg // attribute covariance
5199760c2415Smrg alias int F1();
5200760c2415Smrg static assert(is(DerivedFunctionType!(F1, F1) == F1));
5201760c2415Smrg alias int F2() pure nothrow;
5202760c2415Smrg static assert(is(DerivedFunctionType!(F1, F2) == F2));
5203760c2415Smrg alias int F3() @safe;
5204760c2415Smrg alias int F23() @safe pure nothrow;
5205760c2415Smrg static assert(is(DerivedFunctionType!(F2, F3) == F23));
5206760c2415Smrg
5207760c2415Smrg // return type covariance
5208760c2415Smrg alias long F4();
5209760c2415Smrg static assert(is(DerivedFunctionType!(F1, F4) == void));
5210760c2415Smrg class C {}
5211760c2415Smrg class D : C {}
5212760c2415Smrg alias C F5();
5213760c2415Smrg alias D F6();
5214760c2415Smrg static assert(is(DerivedFunctionType!(F5, F6) == F6));
5215760c2415Smrg alias typeof(null) F7();
5216760c2415Smrg alias int[] F8();
5217760c2415Smrg alias int* F9();
5218760c2415Smrg static assert(is(DerivedFunctionType!(F5, F7) == F7));
5219760c2415Smrg static assert(is(DerivedFunctionType!(F7, F8) == void));
5220760c2415Smrg static assert(is(DerivedFunctionType!(F7, F9) == F7));
5221760c2415Smrg
5222760c2415Smrg // variadic type equality
5223760c2415Smrg alias int F10(int);
5224760c2415Smrg alias int F11(int...);
5225760c2415Smrg alias int F12(int, ...);
5226760c2415Smrg static assert(is(DerivedFunctionType!(F10, F11) == void));
5227760c2415Smrg static assert(is(DerivedFunctionType!(F10, F12) == void));
5228760c2415Smrg static assert(is(DerivedFunctionType!(F11, F12) == void));
5229760c2415Smrg
5230760c2415Smrg // linkage equality
5231760c2415Smrg alias extern(C) int F13(int);
5232760c2415Smrg alias extern(D) int F14(int);
5233760c2415Smrg alias extern(Windows) int F15(int);
5234760c2415Smrg static assert(is(DerivedFunctionType!(F13, F14) == void));
5235760c2415Smrg static assert(is(DerivedFunctionType!(F13, F15) == void));
5236760c2415Smrg static assert(is(DerivedFunctionType!(F14, F15) == void));
5237760c2415Smrg
5238760c2415Smrg // ref & @property equality
5239760c2415Smrg alias int F16(int);
5240760c2415Smrg alias ref int F17(int);
5241760c2415Smrg alias @property int F18(int);
5242760c2415Smrg static assert(is(DerivedFunctionType!(F16, F17) == void));
5243760c2415Smrg static assert(is(DerivedFunctionType!(F16, F18) == void));
5244760c2415Smrg static assert(is(DerivedFunctionType!(F17, F18) == void));
5245760c2415Smrg }
5246760c2415Smrg
5247760c2415Smrg package template staticIota(int beg, int end)
5248760c2415Smrg {
5249760c2415Smrg static if (beg + 1 >= end)
5250760c2415Smrg {
5251760c2415Smrg static if (beg >= end)
5252760c2415Smrg {
5253760c2415Smrg alias staticIota = AliasSeq!();
5254760c2415Smrg }
5255760c2415Smrg else
5256760c2415Smrg {
5257760c2415Smrg alias staticIota = AliasSeq!(+beg);
5258760c2415Smrg }
5259760c2415Smrg }
5260760c2415Smrg else
5261760c2415Smrg {
5262760c2415Smrg enum mid = beg + (end - beg) / 2;
5263760c2415Smrg alias staticIota = AliasSeq!(staticIota!(beg, mid), staticIota!(mid, end));
5264760c2415Smrg }
5265760c2415Smrg }
5266760c2415Smrg
5267760c2415Smrg package template mixinAll(mixins...)
5268760c2415Smrg {
5269760c2415Smrg static if (mixins.length == 1)
5270760c2415Smrg {
5271760c2415Smrg static if (is(typeof(mixins[0]) == string))
5272760c2415Smrg {
5273760c2415Smrg mixin(mixins[0]);
5274760c2415Smrg }
5275760c2415Smrg else
5276760c2415Smrg {
5277760c2415Smrg alias it = mixins[0];
5278760c2415Smrg mixin it;
5279760c2415Smrg }
5280760c2415Smrg }
5281760c2415Smrg else static if (mixins.length >= 2)
5282760c2415Smrg {
5283760c2415Smrg mixin mixinAll!(mixins[ 0 .. $/2]);
5284760c2415Smrg mixin mixinAll!(mixins[$/2 .. $ ]);
5285760c2415Smrg }
5286760c2415Smrg }
5287760c2415Smrg
5288760c2415Smrg package template Bind(alias Template, args1...)
5289760c2415Smrg {
5290760c2415Smrg alias Bind(args2...) = Template!(args1, args2);
5291760c2415Smrg }
5292760c2415Smrg
5293760c2415Smrg
5294760c2415Smrg /**
5295760c2415Smrg Options regarding auto-initialization of a $(D RefCounted) object (see
5296760c2415Smrg the definition of $(D RefCounted) below).
5297760c2415Smrg */
5298760c2415Smrg enum RefCountedAutoInitialize
5299760c2415Smrg {
5300760c2415Smrg /// Do not auto-initialize the object
5301760c2415Smrg no,
5302760c2415Smrg /// Auto-initialize the object
5303760c2415Smrg yes,
5304760c2415Smrg }
5305760c2415Smrg
5306760c2415Smrg /**
5307760c2415Smrg Defines a reference-counted object containing a $(D T) value as
5308760c2415Smrg payload.
5309760c2415Smrg
5310760c2415Smrg An instance of $(D RefCounted) is a reference to a structure,
5311760c2415Smrg which is referred to as the $(I store), or $(I storage implementation
5312760c2415Smrg struct) in this documentation. The store contains a reference count
5313760c2415Smrg and the $(D T) payload. $(D RefCounted) uses $(D malloc) to allocate
5314760c2415Smrg the store. As instances of $(D RefCounted) are copied or go out of
5315760c2415Smrg scope, they will automatically increment or decrement the reference
5316760c2415Smrg count. When the reference count goes down to zero, $(D RefCounted)
5317760c2415Smrg will call $(D destroy) against the payload and call $(D free) to
5318760c2415Smrg deallocate the store. If the $(D T) payload contains any references
5319760c2415Smrg to GC-allocated memory, then `RefCounted` will add it to the GC memory
5320760c2415Smrg that is scanned for pointers, and remove it from GC scanning before
5321760c2415Smrg $(D free) is called on the store.
5322760c2415Smrg
5323760c2415Smrg One important consequence of $(D destroy) is that it will call the
5324760c2415Smrg destructor of the $(D T) payload. GC-managed references are not
5325760c2415Smrg guaranteed to be valid during a destructor call, but other members of
5326760c2415Smrg $(D T), such as file handles or pointers to $(D malloc) memory, will
5327760c2415Smrg still be valid during the destructor call. This allows the $(D T) to
5328760c2415Smrg deallocate or clean up any non-GC resources immediately after the
5329760c2415Smrg reference count has reached zero.
5330760c2415Smrg
5331760c2415Smrg $(D RefCounted) is unsafe and should be used with care. No references
5332760c2415Smrg to the payload should be escaped outside the $(D RefCounted) object.
5333760c2415Smrg
5334760c2415Smrg The $(D autoInit) option makes the object ensure the store is
5335760c2415Smrg automatically initialized. Leaving $(D autoInit ==
5336760c2415Smrg RefCountedAutoInitialize.yes) (the default option) is convenient but
5337760c2415Smrg has the cost of a test whenever the payload is accessed. If $(D
5338760c2415Smrg autoInit == RefCountedAutoInitialize.no), user code must call either
5339760c2415Smrg $(D refCountedStore.isInitialized) or $(D refCountedStore.ensureInitialized)
5340760c2415Smrg before attempting to access the payload. Not doing so results in null
5341760c2415Smrg pointer dereference.
5342760c2415Smrg */
5343760c2415Smrg struct RefCounted(T, RefCountedAutoInitialize autoInit =
5344760c2415Smrg RefCountedAutoInitialize.yes)
5345760c2415Smrg if (!is(T == class) && !(is(T == interface)))
5346760c2415Smrg {
5347760c2415Smrg extern(C) private pure nothrow @nogc static // TODO remove pure when https://issues.dlang.org/show_bug.cgi?id=15862 has been fixed
5348760c2415Smrg {
5349760c2415Smrg pragma(mangle, "free") void pureFree( void *ptr );
5350760c2415Smrg pragma(mangle, "gc_addRange") void pureGcAddRange( in void* p, size_t sz, const TypeInfo ti = null );
5351760c2415Smrg pragma(mangle, "gc_removeRange") void pureGcRemoveRange( in void* p );
5352760c2415Smrg }
5353760c2415Smrg
5354760c2415Smrg /// $(D RefCounted) storage implementation.
5355760c2415Smrg struct RefCountedStore
5356760c2415Smrg {
5357760c2415Smrg import core.memory : pureMalloc;
5358760c2415Smrg private struct Impl
5359760c2415Smrg {
5360760c2415Smrg T _payload;
5361760c2415Smrg size_t _count;
5362760c2415Smrg }
5363760c2415Smrg
5364760c2415Smrg private Impl* _store;
5365760c2415Smrg
5366760c2415Smrg private void initialize(A...)(auto ref A args)
5367760c2415Smrg {
5368760c2415Smrg import core.exception : onOutOfMemoryError;
5369760c2415Smrg import std.conv : emplace;
5370760c2415Smrg
5371760c2415Smrg _store = cast(Impl*) pureMalloc(Impl.sizeof);
5372760c2415Smrg if (_store is null)
5373760c2415Smrg onOutOfMemoryError();
5374760c2415Smrg static if (hasIndirections!T)
5375760c2415Smrg pureGcAddRange(&_store._payload, T.sizeof);
5376760c2415Smrg emplace(&_store._payload, args);
5377760c2415Smrg _store._count = 1;
5378760c2415Smrg }
5379760c2415Smrg
5380760c2415Smrg private void move(ref T source)
5381760c2415Smrg {
5382760c2415Smrg import core.exception : onOutOfMemoryError;
5383760c2415Smrg import core.stdc.string : memcpy, memset;
5384760c2415Smrg
5385760c2415Smrg _store = cast(Impl*) pureMalloc(Impl.sizeof);
5386760c2415Smrg if (_store is null)
5387760c2415Smrg onOutOfMemoryError();
5388760c2415Smrg static if (hasIndirections!T)
5389760c2415Smrg pureGcAddRange(&_store._payload, T.sizeof);
5390760c2415Smrg
5391760c2415Smrg // Can't use std.algorithm.move(source, _store._payload)
5392760c2415Smrg // here because it requires the target to be initialized.
5393760c2415Smrg // Might be worth to add this as `moveEmplace`
5394760c2415Smrg
5395760c2415Smrg // Can avoid destructing result.
5396760c2415Smrg static if (hasElaborateAssign!T || !isAssignable!T)
5397760c2415Smrg memcpy(&_store._payload, &source, T.sizeof);
5398760c2415Smrg else
5399760c2415Smrg _store._payload = source;
5400760c2415Smrg
5401760c2415Smrg // If the source defines a destructor or a postblit hook, we must obliterate the
5402760c2415Smrg // object in order to avoid double freeing and undue aliasing
5403760c2415Smrg static if (hasElaborateDestructor!T || hasElaborateCopyConstructor!T)
5404760c2415Smrg {
5405760c2415Smrg // If T is nested struct, keep original context pointer
5406760c2415Smrg static if (__traits(isNested, T))
5407760c2415Smrg enum sz = T.sizeof - (void*).sizeof;
5408760c2415Smrg else
5409760c2415Smrg enum sz = T.sizeof;
5410760c2415Smrg
5411760c2415Smrg auto init = typeid(T).initializer();
5412760c2415Smrg if (init.ptr is null) // null ptr means initialize to 0s
5413760c2415Smrg memset(&source, 0, sz);
5414760c2415Smrg else
5415760c2415Smrg memcpy(&source, init.ptr, sz);
5416760c2415Smrg }
5417760c2415Smrg
5418760c2415Smrg _store._count = 1;
5419760c2415Smrg }
5420760c2415Smrg
5421760c2415Smrg /**
5422760c2415Smrg Returns $(D true) if and only if the underlying store has been
5423760c2415Smrg allocated and initialized.
5424760c2415Smrg */
5425760c2415Smrg @property nothrow @safe pure @nogc
5426760c2415Smrg bool isInitialized() const
5427760c2415Smrg {
5428760c2415Smrg return _store !is null;
5429760c2415Smrg }
5430760c2415Smrg
5431760c2415Smrg /**
5432760c2415Smrg Returns underlying reference count if it is allocated and initialized
5433760c2415Smrg (a positive integer), and $(D 0) otherwise.
5434760c2415Smrg */
5435760c2415Smrg @property nothrow @safe pure @nogc
5436760c2415Smrg size_t refCount() const
5437760c2415Smrg {
5438760c2415Smrg return isInitialized ? _store._count : 0;
5439760c2415Smrg }
5440760c2415Smrg
5441760c2415Smrg /**
5442760c2415Smrg Makes sure the payload was properly initialized. Such a
5443760c2415Smrg call is typically inserted before using the payload.
5444760c2415Smrg */
5445760c2415Smrg void ensureInitialized()
5446760c2415Smrg {
5447760c2415Smrg if (!isInitialized) initialize();
5448760c2415Smrg }
5449760c2415Smrg
5450760c2415Smrg }
5451760c2415Smrg RefCountedStore _refCounted;
5452760c2415Smrg
5453760c2415Smrg /// Returns storage implementation struct.
5454760c2415Smrg @property nothrow @safe
5455760c2415Smrg ref inout(RefCountedStore) refCountedStore() inout
5456760c2415Smrg {
5457760c2415Smrg return _refCounted;
5458760c2415Smrg }
5459760c2415Smrg
5460760c2415Smrg /**
5461760c2415Smrg Constructor that initializes the payload.
5462760c2415Smrg
5463760c2415Smrg Postcondition: $(D refCountedStore.isInitialized)
5464760c2415Smrg */
5465760c2415Smrg this(A...)(auto ref A args) if (A.length > 0)
5466760c2415Smrg {
5467760c2415Smrg _refCounted.initialize(args);
5468760c2415Smrg }
5469760c2415Smrg
5470760c2415Smrg /// Ditto
5471760c2415Smrg this(T val)
5472760c2415Smrg {
5473760c2415Smrg _refCounted.move(val);
5474760c2415Smrg }
5475760c2415Smrg
5476760c2415Smrg /**
5477760c2415Smrg Constructor that tracks the reference count appropriately. If $(D
5478760c2415Smrg !refCountedStore.isInitialized), does nothing.
5479760c2415Smrg */
5480760c2415Smrg this(this) @safe pure nothrow @nogc
5481760c2415Smrg {
5482760c2415Smrg if (!_refCounted.isInitialized) return;
5483760c2415Smrg ++_refCounted._store._count;
5484760c2415Smrg }
5485760c2415Smrg
5486760c2415Smrg /**
5487760c2415Smrg Destructor that tracks the reference count appropriately. If $(D
5488760c2415Smrg !refCountedStore.isInitialized), does nothing. When the reference count goes
5489760c2415Smrg down to zero, calls $(D destroy) agaist the payload and calls $(D free)
5490760c2415Smrg to deallocate the corresponding resource.
5491760c2415Smrg */
5492760c2415Smrg ~this()
5493760c2415Smrg {
5494760c2415Smrg if (!_refCounted.isInitialized) return;
5495760c2415Smrg assert(_refCounted._store._count > 0);
5496760c2415Smrg if (--_refCounted._store._count)
5497760c2415Smrg return;
5498760c2415Smrg // Done, deallocate
5499760c2415Smrg .destroy(_refCounted._store._payload);
5500760c2415Smrg static if (hasIndirections!T)
5501760c2415Smrg {
5502760c2415Smrg pureGcRemoveRange(&_refCounted._store._payload);
5503760c2415Smrg }
5504760c2415Smrg
5505760c2415Smrg pureFree(_refCounted._store);
5506760c2415Smrg _refCounted._store = null;
5507760c2415Smrg }
5508760c2415Smrg
5509760c2415Smrg /**
5510760c2415Smrg Assignment operators
5511760c2415Smrg */
5512760c2415Smrg void opAssign(typeof(this) rhs)
5513760c2415Smrg {
5514760c2415Smrg import std.algorithm.mutation : swap;
5515760c2415Smrg
5516760c2415Smrg swap(_refCounted._store, rhs._refCounted._store);
5517760c2415Smrg }
5518760c2415Smrg
5519760c2415Smrg /// Ditto
5520760c2415Smrg void opAssign(T rhs)
5521760c2415Smrg {
5522760c2415Smrg import std.algorithm.mutation : move;
5523760c2415Smrg
5524760c2415Smrg static if (autoInit == RefCountedAutoInitialize.yes)
5525760c2415Smrg {
5526760c2415Smrg _refCounted.ensureInitialized();
5527760c2415Smrg }
5528760c2415Smrg else
5529760c2415Smrg {
5530760c2415Smrg assert(_refCounted.isInitialized);
5531760c2415Smrg }
5532760c2415Smrg move(rhs, _refCounted._store._payload);
5533760c2415Smrg }
5534760c2415Smrg
5535760c2415Smrg //version to have a single properly ddoc'ed function (w/ correct sig)
5536760c2415Smrg version (StdDdoc)
5537760c2415Smrg {
5538760c2415Smrg /**
5539760c2415Smrg Returns a reference to the payload. If (autoInit ==
5540760c2415Smrg RefCountedAutoInitialize.yes), calls $(D
5541760c2415Smrg refCountedStore.ensureInitialized). Otherwise, just issues $(D
5542760c2415Smrg assert(refCountedStore.isInitialized)). Used with $(D alias
5543760c2415Smrg refCountedPayload this;), so callers can just use the $(D RefCounted)
5544760c2415Smrg object as a $(D T).
5545760c2415Smrg
5546760c2415Smrg $(BLUE The first overload exists only if $(D autoInit == RefCountedAutoInitialize.yes).)
5547760c2415Smrg So if $(D autoInit == RefCountedAutoInitialize.no)
5548760c2415Smrg or called for a constant or immutable object, then
5549760c2415Smrg $(D refCountedPayload) will also be qualified as safe and nothrow
5550760c2415Smrg (but will still assert if not initialized).
5551760c2415Smrg */
5552760c2415Smrg @property
5553760c2415Smrg ref T refCountedPayload() return;
5554760c2415Smrg
5555760c2415Smrg /// ditto
5556760c2415Smrg @property nothrow @safe pure @nogc
5557760c2415Smrg ref inout(T) refCountedPayload() inout return;
5558760c2415Smrg }
5559760c2415Smrg else
5560760c2415Smrg {
5561760c2415Smrg static if (autoInit == RefCountedAutoInitialize.yes)
5562760c2415Smrg {
5563760c2415Smrg //Can't use inout here because of potential mutation
5564760c2415Smrg @property
5565760c2415Smrg ref T refCountedPayload() return
5566760c2415Smrg {
5567760c2415Smrg _refCounted.ensureInitialized();
5568760c2415Smrg return _refCounted._store._payload;
5569760c2415Smrg }
5570760c2415Smrg }
5571760c2415Smrg
5572760c2415Smrg @property nothrow @safe pure @nogc
5573760c2415Smrg ref inout(T) refCountedPayload() inout return
5574760c2415Smrg {
5575760c2415Smrg assert(_refCounted.isInitialized, "Attempted to access an uninitialized payload.");
5576760c2415Smrg return _refCounted._store._payload;
5577760c2415Smrg }
5578760c2415Smrg }
5579760c2415Smrg
5580760c2415Smrg /**
5581760c2415Smrg Returns a reference to the payload. If (autoInit ==
5582760c2415Smrg RefCountedAutoInitialize.yes), calls $(D
5583760c2415Smrg refCountedStore.ensureInitialized). Otherwise, just issues $(D
5584760c2415Smrg assert(refCountedStore.isInitialized)).
5585760c2415Smrg */
5586760c2415Smrg alias refCountedPayload this;
5587760c2415Smrg }
5588760c2415Smrg
5589760c2415Smrg ///
5590760c2415Smrg pure @system nothrow @nogc unittest
5591760c2415Smrg {
5592760c2415Smrg // A pair of an `int` and a `size_t` - the latter being the
5593760c2415Smrg // reference count - will be dynamically allocated
5594760c2415Smrg auto rc1 = RefCounted!int(5);
5595760c2415Smrg assert(rc1 == 5);
5596760c2415Smrg // No more allocation, add just one extra reference count
5597760c2415Smrg auto rc2 = rc1;
5598760c2415Smrg // Reference semantics
5599760c2415Smrg rc2 = 42;
5600760c2415Smrg assert(rc1 == 42);
5601760c2415Smrg // the pair will be freed when rc1 and rc2 go out of scope
5602760c2415Smrg }
5603760c2415Smrg
5604760c2415Smrg pure @system unittest
5605760c2415Smrg {
5606760c2415Smrg RefCounted!int* p;
5607760c2415Smrg {
5608760c2415Smrg auto rc1 = RefCounted!int(5);
5609760c2415Smrg p = &rc1;
5610760c2415Smrg assert(rc1 == 5);
5611760c2415Smrg assert(rc1._refCounted._store._count == 1);
5612760c2415Smrg auto rc2 = rc1;
5613760c2415Smrg assert(rc1._refCounted._store._count == 2);
5614760c2415Smrg // Reference semantics
5615760c2415Smrg rc2 = 42;
5616760c2415Smrg assert(rc1 == 42);
5617760c2415Smrg rc2 = rc2;
5618760c2415Smrg assert(rc2._refCounted._store._count == 2);
5619760c2415Smrg rc1 = rc2;
5620760c2415Smrg assert(rc1._refCounted._store._count == 2);
5621760c2415Smrg }
5622760c2415Smrg assert(p._refCounted._store == null);
5623760c2415Smrg
5624760c2415Smrg // RefCounted as a member
5625760c2415Smrg struct A
5626760c2415Smrg {
5627760c2415Smrg RefCounted!int x;
5628760c2415Smrg this(int y)
5629760c2415Smrg {
5630760c2415Smrg x._refCounted.initialize(y);
5631760c2415Smrg }
5632760c2415Smrg A copy()
5633760c2415Smrg {
5634760c2415Smrg auto another = this;
5635760c2415Smrg return another;
5636760c2415Smrg }
5637760c2415Smrg }
5638760c2415Smrg auto a = A(4);
5639760c2415Smrg auto b = a.copy();
5640760c2415Smrg assert(a.x._refCounted._store._count == 2, "BUG 4356 still unfixed");
5641760c2415Smrg }
5642760c2415Smrg
5643760c2415Smrg pure @system nothrow @nogc unittest
5644760c2415Smrg {
5645760c2415Smrg import std.algorithm.mutation : swap;
5646760c2415Smrg
5647760c2415Smrg RefCounted!int p1, p2;
5648760c2415Smrg swap(p1, p2);
5649760c2415Smrg }
5650760c2415Smrg
5651760c2415Smrg // 6606
5652760c2415Smrg @safe pure nothrow @nogc unittest
5653760c2415Smrg {
5654760c2415Smrg union U {
5655760c2415Smrg size_t i;
5656760c2415Smrg void* p;
5657760c2415Smrg }
5658760c2415Smrg
5659760c2415Smrg struct S {
5660760c2415Smrg U u;
5661760c2415Smrg }
5662760c2415Smrg
5663760c2415Smrg alias SRC = RefCounted!S;
5664760c2415Smrg }
5665760c2415Smrg
5666760c2415Smrg // 6436
5667760c2415Smrg @system pure unittest
5668760c2415Smrg {
5669760c2415Smrg struct S { this(ref int val) { assert(val == 3); ++val; } }
5670760c2415Smrg
5671760c2415Smrg int val = 3;
5672760c2415Smrg auto s = RefCounted!S(val);
5673760c2415Smrg assert(val == 4);
5674760c2415Smrg }
5675760c2415Smrg
5676760c2415Smrg // gc_addRange coverage
5677760c2415Smrg @system pure unittest
5678760c2415Smrg {
5679760c2415Smrg struct S { int* p; }
5680760c2415Smrg
5681760c2415Smrg auto s = RefCounted!S(null);
5682760c2415Smrg }
5683760c2415Smrg
5684760c2415Smrg @system pure nothrow @nogc unittest
5685760c2415Smrg {
5686760c2415Smrg RefCounted!int a;
5687760c2415Smrg a = 5; //This should not assert
5688760c2415Smrg assert(a == 5);
5689760c2415Smrg
5690760c2415Smrg RefCounted!int b;
5691760c2415Smrg b = a; //This should not assert either
5692760c2415Smrg assert(b == 5);
5693760c2415Smrg
5694760c2415Smrg RefCounted!(int*) c;
5695760c2415Smrg }
5696760c2415Smrg
5697760c2415Smrg /**
5698760c2415Smrg * Initializes a `RefCounted` with `val`. The template parameter
5699760c2415Smrg * `T` of `RefCounted` is inferred from `val`.
5700760c2415Smrg * This function can be used to move non-copyable values to the heap.
5701760c2415Smrg * It also disables the `autoInit` option of `RefCounted`.
5702760c2415Smrg *
5703760c2415Smrg * Params:
5704760c2415Smrg * val = The value to be reference counted
5705760c2415Smrg * Returns:
5706760c2415Smrg * An initialized $(D RefCounted) containing $(D val).
5707760c2415Smrg * See_Also:
5708760c2415Smrg * $(HTTP en.cppreference.com/w/cpp/memory/shared_ptr/make_shared, C++'s make_shared)
5709760c2415Smrg */
5710760c2415Smrg RefCounted!(T, RefCountedAutoInitialize.no) refCounted(T)(T val)
5711760c2415Smrg {
5712760c2415Smrg typeof(return) res;
5713760c2415Smrg res._refCounted.move(val);
5714760c2415Smrg return res;
5715760c2415Smrg }
5716760c2415Smrg
5717760c2415Smrg ///
5718760c2415Smrg @system unittest
5719760c2415Smrg {
5720760c2415Smrg static struct File
5721760c2415Smrg {
5722760c2415Smrg string name;
5723760c2415Smrg @disable this(this); // not copyable
5724760c2415Smrg ~this() { name = null; }
5725760c2415Smrg }
5726760c2415Smrg
5727760c2415Smrg auto file = File("name");
5728760c2415Smrg assert(file.name == "name");
5729760c2415Smrg // file cannot be copied and has unique ownership
5730760c2415Smrg static assert(!__traits(compiles, {auto file2 = file;}));
5731760c2415Smrg
5732760c2415Smrg // make the file refcounted to share ownership
5733760c2415Smrg import std.algorithm.mutation : move;
5734760c2415Smrg auto rcFile = refCounted(move(file));
5735760c2415Smrg assert(rcFile.name == "name");
5736760c2415Smrg assert(file.name == null);
5737760c2415Smrg auto rcFile2 = rcFile;
5738760c2415Smrg assert(rcFile.refCountedStore.refCount == 2);
5739760c2415Smrg // file gets properly closed when last reference is dropped
5740760c2415Smrg }
5741760c2415Smrg
5742760c2415Smrg /**
5743760c2415Smrg Creates a proxy for the value `a` that will forward all operations
5744760c2415Smrg while disabling implicit conversions. The aliased item `a` must be
5745760c2415Smrg an $(B lvalue). This is useful for creating a new type from the
5746760c2415Smrg "base" type (though this is $(B not) a subtype-supertype
5747760c2415Smrg relationship; the new type is not related to the old type in any way,
5748760c2415Smrg by design).
5749760c2415Smrg
5750760c2415Smrg The new type supports all operations that the underlying type does,
5751760c2415Smrg including all operators such as `+`, `--`, `<`, `[]`, etc.
5752760c2415Smrg
5753760c2415Smrg Params:
5754760c2415Smrg a = The value to act as a proxy for all operations. It must
5755760c2415Smrg be an lvalue.
5756760c2415Smrg */
5757760c2415Smrg mixin template Proxy(alias a)
5758760c2415Smrg {
5759760c2415Smrg private alias ValueType = typeof({ return a; }());
5760760c2415Smrg
5761760c2415Smrg /* Determine if 'T.a' can referenced via a const(T).
5762760c2415Smrg * Use T* as the parameter because 'scope' inference needs a fully
5763760c2415Smrg * analyzed T, which doesn't work when accessibleFrom() is used in a
5764760c2415Smrg * 'static if' in the definition of Proxy or T.
5765760c2415Smrg */
5766760c2415Smrg private enum bool accessibleFrom(T) =
5767760c2415Smrg is(typeof((T* self){ cast(void) mixin("(*self)."~__traits(identifier, a)); }));
5768760c2415Smrg
5769760c2415Smrg static if (is(typeof(this) == class))
5770760c2415Smrg {
5771760c2415Smrg override bool opEquals(Object o)
5772760c2415Smrg {
5773760c2415Smrg if (auto b = cast(typeof(this))o)
5774760c2415Smrg {
5775760c2415Smrg return a == mixin("b."~__traits(identifier, a));
5776760c2415Smrg }
5777760c2415Smrg return false;
5778760c2415Smrg }
5779760c2415Smrg
5780760c2415Smrg bool opEquals(T)(T b)
5781760c2415Smrg if (is(ValueType : T) || is(typeof(a.opEquals(b))) || is(typeof(b.opEquals(a))))
5782760c2415Smrg {
5783760c2415Smrg static if (is(typeof(a.opEquals(b))))
5784760c2415Smrg return a.opEquals(b);
5785760c2415Smrg else static if (is(typeof(b.opEquals(a))))
5786760c2415Smrg return b.opEquals(a);
5787760c2415Smrg else
5788760c2415Smrg return a == b;
5789760c2415Smrg }
5790760c2415Smrg
5791760c2415Smrg override int opCmp(Object o)
5792760c2415Smrg {
5793760c2415Smrg if (auto b = cast(typeof(this))o)
5794760c2415Smrg {
5795760c2415Smrg return a < mixin("b."~__traits(identifier, a)) ? -1
5796760c2415Smrg : a > mixin("b."~__traits(identifier, a)) ? +1 : 0;
5797760c2415Smrg }
5798760c2415Smrg static if (is(ValueType == class))
5799760c2415Smrg return a.opCmp(o);
5800760c2415Smrg else
5801760c2415Smrg throw new Exception("Attempt to compare a "~typeid(this).toString~" and a "~typeid(o).toString);
5802760c2415Smrg }
5803760c2415Smrg
5804760c2415Smrg int opCmp(T)(auto ref const T b)
5805760c2415Smrg if (is(ValueType : T) || is(typeof(a.opCmp(b))) || is(typeof(b.opCmp(a))))
5806760c2415Smrg {
5807760c2415Smrg static if (is(typeof(a.opCmp(b))))
5808760c2415Smrg return a.opCmp(b);
5809760c2415Smrg else static if (is(typeof(b.opCmp(a))))
5810760c2415Smrg return -b.opCmp(b);
5811760c2415Smrg else
5812760c2415Smrg return a < b ? -1 : a > b ? +1 : 0;
5813760c2415Smrg }
5814760c2415Smrg
5815760c2415Smrg static if (accessibleFrom!(const typeof(this)))
5816760c2415Smrg {
5817760c2415Smrg override hash_t toHash() const nothrow @trusted
5818760c2415Smrg {
5819760c2415Smrg static if (is(typeof(&a) == ValueType*))
5820760c2415Smrg alias v = a;
5821760c2415Smrg else
5822760c2415Smrg auto v = a; // if a is (property) function
5823760c2415Smrg return typeid(ValueType).getHash(cast(const void*)&v);
5824760c2415Smrg }
5825760c2415Smrg }
5826760c2415Smrg }
5827760c2415Smrg else
5828760c2415Smrg {
5829760c2415Smrg auto ref opEquals(this X, B)(auto ref B b)
5830760c2415Smrg {
5831760c2415Smrg static if (is(immutable B == immutable typeof(this)))
5832760c2415Smrg {
5833760c2415Smrg return a == mixin("b."~__traits(identifier, a));
5834760c2415Smrg }
5835760c2415Smrg else
5836760c2415Smrg return a == b;
5837760c2415Smrg }
5838760c2415Smrg
5839760c2415Smrg auto ref opCmp(this X, B)(auto ref B b)
5840760c2415Smrg if (!is(typeof(a.opCmp(b))) || !is(typeof(b.opCmp(a))))
5841760c2415Smrg {
5842760c2415Smrg static if (is(typeof(a.opCmp(b))))
5843760c2415Smrg return a.opCmp(b);
5844760c2415Smrg else static if (is(typeof(b.opCmp(a))))
5845760c2415Smrg return -b.opCmp(a);
5846760c2415Smrg else static if (isFloatingPoint!ValueType || isFloatingPoint!B)
5847760c2415Smrg return a < b ? -1 : a > b ? +1 : a == b ? 0 : float.nan;
5848760c2415Smrg else
5849760c2415Smrg return a < b ? -1 : (a > b);
5850760c2415Smrg }
5851760c2415Smrg
5852760c2415Smrg static if (accessibleFrom!(const typeof(this)))
5853760c2415Smrg {
5854760c2415Smrg hash_t toHash() const nothrow @trusted
5855760c2415Smrg {
5856760c2415Smrg static if (is(typeof(&a) == ValueType*))
5857760c2415Smrg alias v = a;
5858760c2415Smrg else
5859760c2415Smrg auto v = a; // if a is (property) function
5860760c2415Smrg return typeid(ValueType).getHash(cast(const void*)&v);
5861760c2415Smrg }
5862760c2415Smrg }
5863760c2415Smrg }
5864760c2415Smrg
5865760c2415Smrg auto ref opCall(this X, Args...)(auto ref Args args) { return a(args); }
5866760c2415Smrg
5867760c2415Smrg auto ref opCast(T, this X)() { return cast(T) a; }
5868760c2415Smrg
5869760c2415Smrg auto ref opIndex(this X, D...)(auto ref D i) { return a[i]; }
5870760c2415Smrg auto ref opSlice(this X )() { return a[]; }
5871760c2415Smrg auto ref opSlice(this X, B, E)(auto ref B b, auto ref E e) { return a[b .. e]; }
5872760c2415Smrg
5873760c2415Smrg auto ref opUnary (string op, this X )() { return mixin(op~"a"); }
5874760c2415Smrg auto ref opIndexUnary(string op, this X, D...)(auto ref D i) { return mixin(op~"a[i]"); }
5875760c2415Smrg auto ref opSliceUnary(string op, this X )() { return mixin(op~"a[]"); }
5876760c2415Smrg auto ref opSliceUnary(string op, this X, B, E)(auto ref B b, auto ref E e) { return mixin(op~"a[b .. e]"); }
5877760c2415Smrg
5878760c2415Smrg auto ref opBinary(string op, this X, B)(auto ref B b)
5879760c2415Smrg if (op == "in" && is(typeof(a in b)) || op != "in")
5880760c2415Smrg {
5881760c2415Smrg return mixin("a "~op~" b");
5882760c2415Smrg }
5883760c2415Smrg auto ref opBinaryRight(string op, this X, B)(auto ref B b) { return mixin("b "~op~" a"); }
5884760c2415Smrg
5885760c2415Smrg static if (!is(typeof(this) == class))
5886760c2415Smrg {
5887760c2415Smrg import std.traits;
5888760c2415Smrg static if (isAssignable!ValueType)
5889760c2415Smrg {
5890760c2415Smrg auto ref opAssign(this X)(auto ref typeof(this) v)
5891760c2415Smrg {
5892760c2415Smrg a = mixin("v."~__traits(identifier, a));
5893760c2415Smrg return this;
5894760c2415Smrg }
5895760c2415Smrg }
5896760c2415Smrg else
5897760c2415Smrg {
5898760c2415Smrg @disable void opAssign(this X)(auto ref typeof(this) v);
5899760c2415Smrg }
5900760c2415Smrg }
5901760c2415Smrg
5902760c2415Smrg auto ref opAssign (this X, V )(auto ref V v) if (!is(V == typeof(this))) { return a = v; }
5903760c2415Smrg auto ref opIndexAssign(this X, V, D...)(auto ref V v, auto ref D i) { return a[i] = v; }
5904760c2415Smrg auto ref opSliceAssign(this X, V )(auto ref V v) { return a[] = v; }
5905760c2415Smrg auto ref opSliceAssign(this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e) { return a[b .. e] = v; }
5906760c2415Smrg
5907760c2415Smrg auto ref opOpAssign (string op, this X, V )(auto ref V v)
5908760c2415Smrg {
5909760c2415Smrg return mixin("a " ~op~"= v");
5910760c2415Smrg }
5911760c2415Smrg auto ref opIndexOpAssign(string op, this X, V, D...)(auto ref V v, auto ref D i)
5912760c2415Smrg {
5913760c2415Smrg return mixin("a[i] " ~op~"= v");
5914760c2415Smrg }
5915760c2415Smrg auto ref opSliceOpAssign(string op, this X, V )(auto ref V v)
5916760c2415Smrg {
5917760c2415Smrg return mixin("a[] " ~op~"= v");
5918760c2415Smrg }
5919760c2415Smrg auto ref opSliceOpAssign(string op, this X, V, B, E)(auto ref V v, auto ref B b, auto ref E e)
5920760c2415Smrg {
5921760c2415Smrg return mixin("a[b .. e] "~op~"= v");
5922760c2415Smrg }
5923760c2415Smrg
5924760c2415Smrg template opDispatch(string name)
5925760c2415Smrg {
5926760c2415Smrg static if (is(typeof(__traits(getMember, a, name)) == function))
5927760c2415Smrg {
5928760c2415Smrg // non template function
5929760c2415Smrg auto ref opDispatch(this X, Args...)(auto ref Args args) { return mixin("a."~name~"(args)"); }
5930760c2415Smrg }
5931760c2415Smrg else static if (is(typeof({ enum x = mixin("a."~name); })))
5932760c2415Smrg {
5933760c2415Smrg // built-in type field, manifest constant, and static non-mutable field
5934760c2415Smrg enum opDispatch = mixin("a."~name);
5935760c2415Smrg }
5936*0bfacb9bSmrg else static if (__traits(isTemplate, mixin("a."~name)))
5937760c2415Smrg {
5938760c2415Smrg // member template
5939760c2415Smrg template opDispatch(T...)
5940760c2415Smrg {
5941760c2415Smrg enum targs = T.length ? "!T" : "";
5942760c2415Smrg auto ref opDispatch(this X, Args...)(auto ref Args args){ return mixin("a."~name~targs~"(args)"); }
5943760c2415Smrg }
5944760c2415Smrg }
5945*0bfacb9bSmrg else
5946*0bfacb9bSmrg {
5947*0bfacb9bSmrg // field or property function
5948*0bfacb9bSmrg @property auto ref opDispatch(this X)() { return mixin("a."~name); }
5949*0bfacb9bSmrg @property auto ref opDispatch(this X, V)(auto ref V v) { return mixin("a."~name~" = v"); }
5950*0bfacb9bSmrg }
5951*0bfacb9bSmrg
5952760c2415Smrg }
5953760c2415Smrg
5954760c2415Smrg import std.traits : isArray;
5955760c2415Smrg
5956760c2415Smrg static if (isArray!ValueType)
5957760c2415Smrg {
5958760c2415Smrg auto opDollar() const { return a.length; }
5959760c2415Smrg }
5960760c2415Smrg else static if (is(typeof(a.opDollar!0)))
5961760c2415Smrg {
5962760c2415Smrg auto ref opDollar(size_t pos)() { return a.opDollar!pos(); }
5963760c2415Smrg }
5964760c2415Smrg else static if (is(typeof(a.opDollar) == function))
5965760c2415Smrg {
5966760c2415Smrg auto ref opDollar() { return a.opDollar(); }
5967760c2415Smrg }
5968760c2415Smrg else static if (is(typeof(a.opDollar)))
5969760c2415Smrg {
5970760c2415Smrg alias opDollar = a.opDollar;
5971760c2415Smrg }
5972760c2415Smrg }
5973760c2415Smrg
5974760c2415Smrg ///
5975760c2415Smrg @safe unittest
5976760c2415Smrg {
5977760c2415Smrg struct MyInt
5978760c2415Smrg {
5979760c2415Smrg private int value;
5980760c2415Smrg mixin Proxy!value;
5981760c2415Smrg
5982760c2415Smrg this(int n){ value = n; }
5983760c2415Smrg }
5984760c2415Smrg
5985760c2415Smrg MyInt n = 10;
5986760c2415Smrg
5987760c2415Smrg // Enable operations that original type has.
5988760c2415Smrg ++n;
5989760c2415Smrg assert(n == 11);
5990760c2415Smrg assert(n * 2 == 22);
5991760c2415Smrg
5992760c2415Smrg void func(int n) { }
5993760c2415Smrg
5994760c2415Smrg // Disable implicit conversions to original type.
5995760c2415Smrg //int x = n;
5996760c2415Smrg //func(n);
5997760c2415Smrg }
5998760c2415Smrg
5999760c2415Smrg ///The proxied value must be an $(B lvalue).
6000760c2415Smrg @safe unittest
6001760c2415Smrg {
6002760c2415Smrg struct NewIntType
6003760c2415Smrg {
6004760c2415Smrg //Won't work; the literal '1'
6005760c2415Smrg //is an rvalue, not an lvalue
6006760c2415Smrg //mixin Proxy!1;
6007760c2415Smrg
6008760c2415Smrg //Okay, n is an lvalue
6009760c2415Smrg int n;
6010760c2415Smrg mixin Proxy!n;
6011760c2415Smrg
6012760c2415Smrg this(int n) { this.n = n; }
6013760c2415Smrg }
6014760c2415Smrg
6015760c2415Smrg NewIntType nit = 0;
6016760c2415Smrg nit++;
6017760c2415Smrg assert(nit == 1);
6018760c2415Smrg
6019760c2415Smrg
6020760c2415Smrg struct NewObjectType
6021760c2415Smrg {
6022760c2415Smrg Object obj;
6023760c2415Smrg //Ok, obj is an lvalue
6024760c2415Smrg mixin Proxy!obj;
6025760c2415Smrg
6026760c2415Smrg this (Object o) { obj = o; }
6027760c2415Smrg }
6028760c2415Smrg
6029760c2415Smrg NewObjectType not = new Object();
6030760c2415Smrg assert(__traits(compiles, not.toHash()));
6031760c2415Smrg }
6032760c2415Smrg
6033760c2415Smrg /**
6034760c2415Smrg There is one exception to the fact that the new type is not related to the
6035760c2415Smrg old type. $(DDSUBLINK spec/function,pseudo-member, Pseudo-member)
6036760c2415Smrg functions are usable with the new type; they will be forwarded on to the
6037760c2415Smrg proxied value.
6038760c2415Smrg */
6039760c2415Smrg @safe unittest
6040760c2415Smrg {
6041760c2415Smrg import std.math;
6042760c2415Smrg
6043760c2415Smrg float f = 1.0;
6044760c2415Smrg assert(!f.isInfinity);
6045760c2415Smrg
6046760c2415Smrg struct NewFloat
6047760c2415Smrg {
6048760c2415Smrg float _;
6049760c2415Smrg mixin Proxy!_;
6050760c2415Smrg
6051760c2415Smrg this(float f) { _ = f; }
6052760c2415Smrg }
6053760c2415Smrg
6054760c2415Smrg NewFloat nf = 1.0f;
6055760c2415Smrg assert(!nf.isInfinity);
6056760c2415Smrg }
6057760c2415Smrg
6058760c2415Smrg @safe unittest
6059760c2415Smrg {
6060760c2415Smrg static struct MyInt
6061760c2415Smrg {
6062760c2415Smrg private int value;
6063760c2415Smrg mixin Proxy!value;
6064760c2415Smrg this(int n) inout { value = n; }
6065760c2415Smrg
6066760c2415Smrg enum str = "str";
6067760c2415Smrg static immutable arr = [1,2,3];
6068760c2415Smrg }
6069760c2415Smrg
6070760c2415Smrg foreach (T; AliasSeq!(MyInt, const MyInt, immutable MyInt))
6071760c2415Smrg {
6072760c2415Smrg T m = 10;
6073760c2415Smrg static assert(!__traits(compiles, { int x = m; }));
6074760c2415Smrg static assert(!__traits(compiles, { void func(int n){} func(m); }));
6075760c2415Smrg assert(m == 10);
6076760c2415Smrg assert(m != 20);
6077760c2415Smrg assert(m < 20);
6078760c2415Smrg assert(+m == 10);
6079760c2415Smrg assert(-m == -10);
6080760c2415Smrg assert(cast(double) m == 10.0);
6081760c2415Smrg assert(m + 10 == 20);
6082760c2415Smrg assert(m - 5 == 5);
6083760c2415Smrg assert(m * 20 == 200);
6084760c2415Smrg assert(m / 2 == 5);
6085760c2415Smrg assert(10 + m == 20);
6086760c2415Smrg assert(15 - m == 5);
6087760c2415Smrg assert(20 * m == 200);
6088760c2415Smrg assert(50 / m == 5);
6089760c2415Smrg static if (is(T == MyInt)) // mutable
6090760c2415Smrg {
6091760c2415Smrg assert(++m == 11);
6092760c2415Smrg assert(m++ == 11); assert(m == 12);
6093760c2415Smrg assert(--m == 11);
6094760c2415Smrg assert(m-- == 11); assert(m == 10);
6095760c2415Smrg m = m;
6096760c2415Smrg m = 20; assert(m == 20);
6097760c2415Smrg }
6098760c2415Smrg static assert(T.max == int.max);
6099760c2415Smrg static assert(T.min == int.min);
6100760c2415Smrg static assert(T.init == int.init);
6101760c2415Smrg static assert(T.str == "str");
6102760c2415Smrg static assert(T.arr == [1,2,3]);
6103760c2415Smrg }
6104760c2415Smrg }
6105760c2415Smrg @system unittest
6106760c2415Smrg {
6107760c2415Smrg static struct MyArray
6108760c2415Smrg {
6109760c2415Smrg private int[] value;
6110760c2415Smrg mixin Proxy!value;
6111760c2415Smrg this(int[] arr) { value = arr; }
6112760c2415Smrg this(immutable int[] arr) immutable { value = arr; }
6113760c2415Smrg }
6114760c2415Smrg
6115760c2415Smrg foreach (T; AliasSeq!(MyArray, const MyArray, immutable MyArray))
6116760c2415Smrg {
6117760c2415Smrg static if (is(T == immutable) && !is(typeof({ T a = [1,2,3,4]; })))
6118760c2415Smrg T a = [1,2,3,4].idup; // workaround until qualified ctor is properly supported
6119760c2415Smrg else
6120760c2415Smrg T a = [1,2,3,4];
6121760c2415Smrg assert(a == [1,2,3,4]);
6122760c2415Smrg assert(a != [5,6,7,8]);
6123760c2415Smrg assert(+a[0] == 1);
6124760c2415Smrg version (LittleEndian)
6125760c2415Smrg assert(cast(ulong[]) a == [0x0000_0002_0000_0001, 0x0000_0004_0000_0003]);
6126760c2415Smrg else
6127760c2415Smrg assert(cast(ulong[]) a == [0x0000_0001_0000_0002, 0x0000_0003_0000_0004]);
6128760c2415Smrg assert(a ~ [10,11] == [1,2,3,4,10,11]);
6129760c2415Smrg assert(a[0] == 1);
6130760c2415Smrg assert(a[] == [1,2,3,4]);
6131760c2415Smrg assert(a[2 .. 4] == [3,4]);
6132760c2415Smrg static if (is(T == MyArray)) // mutable
6133760c2415Smrg {
6134760c2415Smrg a = a;
6135760c2415Smrg a = [5,6,7,8]; assert(a == [5,6,7,8]);
6136760c2415Smrg a[0] = 0; assert(a == [0,6,7,8]);
6137760c2415Smrg a[] = 1; assert(a == [1,1,1,1]);
6138760c2415Smrg a[0 .. 3] = 2; assert(a == [2,2,2,1]);
6139760c2415Smrg a[0] += 2; assert(a == [4,2,2,1]);
6140760c2415Smrg a[] *= 2; assert(a == [8,4,4,2]);
6141760c2415Smrg a[0 .. 2] /= 2; assert(a == [4,2,4,2]);
6142760c2415Smrg }
6143760c2415Smrg }
6144760c2415Smrg }
6145760c2415Smrg @system unittest
6146760c2415Smrg {
6147760c2415Smrg class Foo
6148760c2415Smrg {
6149760c2415Smrg int field;
6150760c2415Smrg
6151760c2415Smrg @property int val1() const { return field; }
6152760c2415Smrg @property void val1(int n) { field = n; }
6153760c2415Smrg
6154760c2415Smrg @property ref int val2() { return field; }
6155760c2415Smrg
6156760c2415Smrg int func(int x, int y) const { return x; }
6157760c2415Smrg void func1(ref int a) { a = 9; }
6158760c2415Smrg
6159760c2415Smrg T ifti1(T)(T t) { return t; }
6160760c2415Smrg void ifti2(Args...)(Args args) { }
6161760c2415Smrg void ifti3(T, Args...)(Args args) { }
6162760c2415Smrg
6163760c2415Smrg T opCast(T)(){ return T.init; }
6164760c2415Smrg
6165760c2415Smrg T tempfunc(T)() { return T.init; }
6166760c2415Smrg }
6167760c2415Smrg class Hoge
6168760c2415Smrg {
6169760c2415Smrg Foo foo;
6170760c2415Smrg mixin Proxy!foo;
6171760c2415Smrg this(Foo f) { foo = f; }
6172760c2415Smrg }
6173760c2415Smrg
6174760c2415Smrg auto h = new Hoge(new Foo());
6175760c2415Smrg int n;
6176760c2415Smrg
6177760c2415Smrg static assert(!__traits(compiles, { Foo f = h; }));
6178760c2415Smrg
6179760c2415Smrg // field
6180760c2415Smrg h.field = 1; // lhs of assign
6181760c2415Smrg n = h.field; // rhs of assign
6182760c2415Smrg assert(h.field == 1); // lhs of BinExp
6183760c2415Smrg assert(1 == h.field); // rhs of BinExp
6184760c2415Smrg assert(n == 1);
6185760c2415Smrg
6186760c2415Smrg // getter/setter property function
6187760c2415Smrg h.val1 = 4;
6188760c2415Smrg n = h.val1;
6189760c2415Smrg assert(h.val1 == 4);
6190760c2415Smrg assert(4 == h.val1);
6191760c2415Smrg assert(n == 4);
6192760c2415Smrg
6193760c2415Smrg // ref getter property function
6194760c2415Smrg h.val2 = 8;
6195760c2415Smrg n = h.val2;
6196760c2415Smrg assert(h.val2 == 8);
6197760c2415Smrg assert(8 == h.val2);
6198760c2415Smrg assert(n == 8);
6199760c2415Smrg
6200760c2415Smrg // member function
6201760c2415Smrg assert(h.func(2,4) == 2);
6202760c2415Smrg h.func1(n);
6203760c2415Smrg assert(n == 9);
6204760c2415Smrg
6205760c2415Smrg // IFTI
6206760c2415Smrg assert(h.ifti1(4) == 4);
6207760c2415Smrg h.ifti2(4);
6208760c2415Smrg h.ifti3!int(4, 3);
6209760c2415Smrg
6210760c2415Smrg // bug5896 test
6211760c2415Smrg assert(h.opCast!int() == 0);
6212760c2415Smrg assert(cast(int) h == 0);
6213760c2415Smrg const ih = new const Hoge(new Foo());
6214760c2415Smrg static assert(!__traits(compiles, ih.opCast!int()));
6215760c2415Smrg static assert(!__traits(compiles, cast(int) ih));
6216760c2415Smrg
6217760c2415Smrg // template member function
6218760c2415Smrg assert(h.tempfunc!int() == 0);
6219760c2415Smrg }
6220760c2415Smrg
6221760c2415Smrg @system unittest // about Proxy inside a class
6222760c2415Smrg {
6223760c2415Smrg class MyClass
6224760c2415Smrg {
6225760c2415Smrg int payload;
6226760c2415Smrg mixin Proxy!payload;
6227760c2415Smrg this(int i){ payload = i; }
6228760c2415Smrg string opCall(string msg){ return msg; }
6229760c2415Smrg int pow(int i){ return payload ^^ i; }
6230760c2415Smrg }
6231760c2415Smrg
6232760c2415Smrg class MyClass2
6233760c2415Smrg {
6234760c2415Smrg MyClass payload;
6235760c2415Smrg mixin Proxy!payload;
6236760c2415Smrg this(int i){ payload = new MyClass(i); }
6237760c2415Smrg }
6238760c2415Smrg
6239760c2415Smrg class MyClass3
6240760c2415Smrg {
6241760c2415Smrg int payload;
6242760c2415Smrg mixin Proxy!payload;
6243760c2415Smrg this(int i){ payload = i; }
6244760c2415Smrg }
6245760c2415Smrg
6246760c2415Smrg // opEquals
6247760c2415Smrg Object a = new MyClass(5);
6248760c2415Smrg Object b = new MyClass(5);
6249760c2415Smrg Object c = new MyClass2(5);
6250760c2415Smrg Object d = new MyClass3(5);
6251760c2415Smrg assert(a == b);
6252760c2415Smrg assert((cast(MyClass) a) == 5);
6253760c2415Smrg assert(5 == (cast(MyClass) b));
6254760c2415Smrg assert(5 == cast(MyClass2) c);
6255760c2415Smrg assert(a != d);
6256760c2415Smrg
6257760c2415Smrg assert(c != a);
6258760c2415Smrg // oops! above line is unexpected, isn't it?
6259760c2415Smrg // the reason is below.
6260760c2415Smrg // MyClass2.opEquals knows MyClass but,
6261760c2415Smrg // MyClass.opEquals doesn't know MyClass2.
6262760c2415Smrg // so, c.opEquals(a) is true, but a.opEquals(c) is false.
6263760c2415Smrg // furthermore, opEquals(T) couldn't be invoked.
6264760c2415Smrg assert((cast(MyClass2) c) != (cast(MyClass) a));
6265760c2415Smrg
6266760c2415Smrg // opCmp
6267760c2415Smrg Object e = new MyClass2(7);
6268760c2415Smrg assert(a < cast(MyClass2) e); // OK. and
6269760c2415Smrg assert(e > a); // OK, but...
6270760c2415Smrg // assert(a < e); // RUNTIME ERROR!
6271760c2415Smrg // assert((cast(MyClass) a) < e); // RUNTIME ERROR!
6272760c2415Smrg assert(3 < cast(MyClass) a);
6273760c2415Smrg assert((cast(MyClass2) e) < 11);
6274760c2415Smrg
6275760c2415Smrg // opCall
6276760c2415Smrg assert((cast(MyClass2) e)("hello") == "hello");
6277760c2415Smrg
6278760c2415Smrg // opCast
6279760c2415Smrg assert((cast(MyClass)(cast(MyClass2) c)) == a);
6280760c2415Smrg assert((cast(int)(cast(MyClass2) c)) == 5);
6281760c2415Smrg
6282760c2415Smrg // opIndex
6283760c2415Smrg class MyClass4
6284760c2415Smrg {
6285760c2415Smrg string payload;
6286760c2415Smrg mixin Proxy!payload;
6287760c2415Smrg this(string s){ payload = s; }
6288760c2415Smrg }
6289760c2415Smrg class MyClass5
6290760c2415Smrg {
6291760c2415Smrg MyClass4 payload;
6292760c2415Smrg mixin Proxy!payload;
6293760c2415Smrg this(string s){ payload = new MyClass4(s); }
6294760c2415Smrg }
6295760c2415Smrg auto f = new MyClass4("hello");
6296760c2415Smrg assert(f[1] == 'e');
6297760c2415Smrg auto g = new MyClass5("hello");
6298760c2415Smrg assert(f[1] == 'e');
6299760c2415Smrg
6300760c2415Smrg // opSlice
6301760c2415Smrg assert(f[2 .. 4] == "ll");
6302760c2415Smrg
6303760c2415Smrg // opUnary
6304760c2415Smrg assert(-(cast(MyClass2) c) == -5);
6305760c2415Smrg
6306760c2415Smrg // opBinary
6307760c2415Smrg assert((cast(MyClass) a) + (cast(MyClass2) c) == 10);
6308760c2415Smrg assert(5 + cast(MyClass) a == 10);
6309760c2415Smrg
6310760c2415Smrg // opAssign
6311760c2415Smrg (cast(MyClass2) c) = 11;
6312760c2415Smrg assert((cast(MyClass2) c) == 11);
6313760c2415Smrg (cast(MyClass2) c) = new MyClass(13);
6314760c2415Smrg assert((cast(MyClass2) c) == 13);
6315760c2415Smrg
6316760c2415Smrg // opOpAssign
6317760c2415Smrg assert((cast(MyClass2) c) += 4);
6318760c2415Smrg assert((cast(MyClass2) c) == 17);
6319760c2415Smrg
6320760c2415Smrg // opDispatch
6321760c2415Smrg assert((cast(MyClass2) c).pow(2) == 289);
6322760c2415Smrg
6323760c2415Smrg // opDollar
6324760c2415Smrg assert(f[2..$-1] == "ll");
6325760c2415Smrg
6326760c2415Smrg // toHash
6327760c2415Smrg int[Object] hash;
6328760c2415Smrg hash[a] = 19;
6329760c2415Smrg hash[c] = 21;
6330760c2415Smrg assert(hash[b] == 19);
6331760c2415Smrg assert(hash[c] == 21);
6332760c2415Smrg }
6333760c2415Smrg
6334760c2415Smrg @safe unittest
6335760c2415Smrg {
6336760c2415Smrg struct MyInt
6337760c2415Smrg {
6338760c2415Smrg int payload;
6339760c2415Smrg
6340760c2415Smrg mixin Proxy!payload;
6341760c2415Smrg }
6342760c2415Smrg
6343760c2415Smrg MyInt v;
6344760c2415Smrg v = v;
6345760c2415Smrg
6346760c2415Smrg struct Foo
6347760c2415Smrg {
6348760c2415Smrg @disable void opAssign(typeof(this));
6349760c2415Smrg }
6350760c2415Smrg struct MyFoo
6351760c2415Smrg {
6352760c2415Smrg Foo payload;
6353760c2415Smrg
6354760c2415Smrg mixin Proxy!payload;
6355760c2415Smrg }
6356760c2415Smrg MyFoo f;
6357760c2415Smrg static assert(!__traits(compiles, f = f));
6358760c2415Smrg
6359760c2415Smrg struct MyFoo2
6360760c2415Smrg {
6361760c2415Smrg Foo payload;
6362760c2415Smrg
6363760c2415Smrg mixin Proxy!payload;
6364760c2415Smrg
6365760c2415Smrg // override default Proxy behavior
6366760c2415Smrg void opAssign(typeof(this) rhs){}
6367760c2415Smrg }
6368760c2415Smrg MyFoo2 f2;
6369760c2415Smrg f2 = f2;
6370760c2415Smrg }
6371760c2415Smrg @safe unittest
6372760c2415Smrg {
6373760c2415Smrg // bug8613
6374760c2415Smrg static struct Name
6375760c2415Smrg {
6376760c2415Smrg mixin Proxy!val;
6377760c2415Smrg private string val;
6378760c2415Smrg this(string s) { val = s; }
6379760c2415Smrg }
6380760c2415Smrg
6381760c2415Smrg bool[Name] names;
6382760c2415Smrg names[Name("a")] = true;
6383760c2415Smrg bool* b = Name("a") in names;
6384760c2415Smrg }
6385760c2415Smrg
6386760c2415Smrg @system unittest
6387760c2415Smrg {
6388760c2415Smrg // bug14213, using function for the payload
6389760c2415Smrg static struct S
6390760c2415Smrg {
6391760c2415Smrg int foo() { return 12; }
6392760c2415Smrg mixin Proxy!foo;
6393760c2415Smrg }
6394760c2415Smrg static class C
6395760c2415Smrg {
6396760c2415Smrg int foo() { return 12; }
6397760c2415Smrg mixin Proxy!foo;
6398760c2415Smrg }
6399760c2415Smrg S s;
6400760c2415Smrg assert(s + 1 == 13);
6401760c2415Smrg C c = new C();
6402760c2415Smrg assert(s * 2 == 24);
6403760c2415Smrg }
6404760c2415Smrg
6405760c2415Smrg // Check all floating point comparisons for both Proxy and Typedef,
6406760c2415Smrg // also against int and a Typedef!int, to be as regression-proof
6407760c2415Smrg // as possible. bug 15561
6408760c2415Smrg @safe unittest
6409760c2415Smrg {
6410760c2415Smrg static struct MyFloatImpl
6411760c2415Smrg {
6412760c2415Smrg float value;
6413760c2415Smrg mixin Proxy!value;
6414760c2415Smrg }
6415760c2415Smrg static void allFail(T0, T1)(T0 a, T1 b)
6416760c2415Smrg {
6417760c2415Smrg assert(!(a == b));
6418760c2415Smrg assert(!(a<b));
6419760c2415Smrg assert(!(a <= b));
6420760c2415Smrg assert(!(a>b));
6421760c2415Smrg assert(!(a >= b));
6422760c2415Smrg }
6423760c2415Smrg foreach (T1; AliasSeq!(MyFloatImpl, Typedef!float, Typedef!double,
6424760c2415Smrg float, real, Typedef!int, int))
6425760c2415Smrg {
6426760c2415Smrg foreach (T2; AliasSeq!(MyFloatImpl, Typedef!float))
6427760c2415Smrg {
6428760c2415Smrg T1 a;
6429760c2415Smrg T2 b;
6430760c2415Smrg
6431760c2415Smrg static if (isFloatingPoint!T1 || isFloatingPoint!(TypedefType!T1))
6432760c2415Smrg allFail(a, b);
6433760c2415Smrg a = 3;
6434760c2415Smrg allFail(a, b);
6435760c2415Smrg
6436760c2415Smrg b = 4;
6437760c2415Smrg assert(a != b);
6438760c2415Smrg assert(a<b);
6439760c2415Smrg assert(a <= b);
6440760c2415Smrg assert(!(a>b));
6441760c2415Smrg assert(!(a >= b));
6442760c2415Smrg
6443760c2415Smrg a = 4;
6444760c2415Smrg assert(a == b);
6445760c2415Smrg assert(!(a<b));
6446760c2415Smrg assert(a <= b);
6447760c2415Smrg assert(!(a>b));
6448760c2415Smrg assert(a >= b);
6449760c2415Smrg }
6450760c2415Smrg }
6451760c2415Smrg }
6452760c2415Smrg
6453760c2415Smrg /**
6454760c2415Smrg $(B Typedef) allows the creation of a unique type which is
6455760c2415Smrg based on an existing type. Unlike the $(D alias) feature,
6456760c2415Smrg $(B Typedef) ensures the two types are not considered as equals.
6457760c2415Smrg
6458760c2415Smrg Example:
6459760c2415Smrg ----
6460760c2415Smrg alias MyInt = Typedef!int;
6461760c2415Smrg static void takeInt(int) { }
6462760c2415Smrg static void takeMyInt(MyInt) { }
6463760c2415Smrg
6464760c2415Smrg int i;
6465760c2415Smrg takeInt(i); // ok
6466760c2415Smrg takeMyInt(i); // fails
6467760c2415Smrg
6468760c2415Smrg MyInt myInt;
6469760c2415Smrg takeInt(myInt); // fails
6470760c2415Smrg takeMyInt(myInt); // ok
6471760c2415Smrg ----
6472760c2415Smrg
6473760c2415Smrg Params:
6474760c2415Smrg
6475760c2415Smrg init = Optional initial value for the new type. For example:
6476760c2415Smrg
6477760c2415Smrg ----
6478760c2415Smrg alias MyInt = Typedef!(int, 10);
6479760c2415Smrg MyInt myInt;
6480760c2415Smrg assert(myInt == 10); // default-initialized to 10
6481760c2415Smrg ----
6482760c2415Smrg
6483760c2415Smrg cookie = Optional, used to create multiple unique types which are
6484760c2415Smrg based on the same origin type $(D T). For example:
6485760c2415Smrg
6486760c2415Smrg ----
6487760c2415Smrg alias TypeInt1 = Typedef!int;
6488760c2415Smrg alias TypeInt2 = Typedef!int;
6489760c2415Smrg
6490760c2415Smrg // The two Typedefs are the same type.
6491760c2415Smrg static assert(is(TypeInt1 == TypeInt2));
6492760c2415Smrg
6493760c2415Smrg alias MoneyEuros = Typedef!(float, float.init, "euros");
6494760c2415Smrg alias MoneyDollars = Typedef!(float, float.init, "dollars");
6495760c2415Smrg
6496760c2415Smrg // The two Typedefs are _not_ the same type.
6497760c2415Smrg static assert(!is(MoneyEuros == MoneyDollars));
6498760c2415Smrg ----
6499760c2415Smrg
6500760c2415Smrg Note: If a library routine cannot handle the Typedef type,
6501760c2415Smrg you can use the $(D TypedefType) template to extract the
6502760c2415Smrg type which the Typedef wraps.
6503760c2415Smrg */
6504760c2415Smrg struct Typedef(T, T init = T.init, string cookie=null)
6505760c2415Smrg {
6506760c2415Smrg private T Typedef_payload = init;
6507760c2415Smrg
6508760c2415Smrg this(T init)
6509760c2415Smrg {
6510760c2415Smrg Typedef_payload = init;
6511760c2415Smrg }
6512760c2415Smrg
6513760c2415Smrg this(Typedef tdef)
6514760c2415Smrg {
6515760c2415Smrg this(tdef.Typedef_payload);
6516760c2415Smrg }
6517760c2415Smrg
6518760c2415Smrg // We need to add special overload for cast(Typedef!X) exp,
6519760c2415Smrg // thus we can't simply inherit Proxy!Typedef_payload
6520760c2415Smrg T2 opCast(T2 : Typedef!(T, Unused), this X, T, Unused...)()
6521760c2415Smrg {
6522760c2415Smrg return T2(cast(T) Typedef_payload);
6523760c2415Smrg }
6524760c2415Smrg
6525760c2415Smrg auto ref opCast(T2, this X)()
6526760c2415Smrg {
6527760c2415Smrg return cast(T2) Typedef_payload;
6528760c2415Smrg }
6529760c2415Smrg
6530760c2415Smrg mixin Proxy!Typedef_payload;
6531760c2415Smrg
6532760c2415Smrg pure nothrow @nogc @safe @property
6533760c2415Smrg {
6534760c2415Smrg alias TD = typeof(this);
6535760c2415Smrg static if (isIntegral!T)
6536760c2415Smrg {
6537760c2415Smrg static TD min() {return TD(T.min);}
6538760c2415Smrg static TD max() {return TD(T.max);}
6539760c2415Smrg }
6540760c2415Smrg else static if (isFloatingPoint!T)
6541760c2415Smrg {
6542760c2415Smrg static TD infinity() {return TD(T.infinity);}
6543760c2415Smrg static TD nan() {return TD(T.nan);}
6544760c2415Smrg static TD dig() {return TD(T.dig);}
6545760c2415Smrg static TD epsilon() {return TD(T.epsilon);}
6546760c2415Smrg static TD mant_dig() {return TD(T.mant_dig);}
6547760c2415Smrg static TD max_10_exp() {return TD(T.max_10_exp);}
6548760c2415Smrg static TD max_exp() {return TD(T.max_exp);}
6549760c2415Smrg static TD min_10_exp() {return TD(T.min_10_exp);}
6550760c2415Smrg static TD min_exp() {return TD(T.min_exp);}
6551760c2415Smrg static TD max() {return TD(T.max);}
6552760c2415Smrg static TD min_normal() {return TD(T.min_normal);}
6553760c2415Smrg TD re() {return TD(Typedef_payload.re);}
6554760c2415Smrg TD im() {return TD(Typedef_payload.im);}
6555760c2415Smrg }
6556760c2415Smrg }
6557760c2415Smrg }
6558760c2415Smrg
6559760c2415Smrg /**
6560760c2415Smrg Get the underlying type which a $(D Typedef) wraps.
6561760c2415Smrg If $(D T) is not a $(D Typedef) it will alias itself to $(D T).
6562760c2415Smrg */
6563760c2415Smrg template TypedefType(T)
6564760c2415Smrg {
6565760c2415Smrg static if (is(T : Typedef!Arg, Arg))
6566760c2415Smrg alias TypedefType = Arg;
6567760c2415Smrg else
6568760c2415Smrg alias TypedefType = T;
6569760c2415Smrg }
6570760c2415Smrg
6571760c2415Smrg ///
6572760c2415Smrg @safe unittest
6573760c2415Smrg {
6574760c2415Smrg import std.typecons : Typedef, TypedefType;
6575760c2415Smrg import std.conv : to;
6576760c2415Smrg
6577760c2415Smrg alias MyInt = Typedef!int;
6578760c2415Smrg static assert(is(TypedefType!MyInt == int));
6579760c2415Smrg
6580760c2415Smrg /// Instantiating with a non-Typedef will return that type
6581760c2415Smrg static assert(is(TypedefType!int == int));
6582760c2415Smrg
6583760c2415Smrg string num = "5";
6584760c2415Smrg
6585760c2415Smrg // extract the needed type
6586760c2415Smrg MyInt myInt = MyInt( num.to!(TypedefType!MyInt) );
6587760c2415Smrg assert(myInt == 5);
6588760c2415Smrg
6589760c2415Smrg // cast to the underlying type to get the value that's being wrapped
6590760c2415Smrg int x = cast(TypedefType!MyInt) myInt;
6591760c2415Smrg
6592760c2415Smrg alias MyIntInit = Typedef!(int, 42);
6593760c2415Smrg static assert(is(TypedefType!MyIntInit == int));
6594760c2415Smrg static assert(MyIntInit() == 42);
6595760c2415Smrg }
6596760c2415Smrg
6597760c2415Smrg @safe unittest
6598760c2415Smrg {
6599760c2415Smrg Typedef!int x = 10;
6600760c2415Smrg static assert(!__traits(compiles, { int y = x; }));
6601760c2415Smrg static assert(!__traits(compiles, { long z = x; }));
6602760c2415Smrg
6603760c2415Smrg Typedef!int y = 10;
6604760c2415Smrg assert(x == y);
6605760c2415Smrg
6606760c2415Smrg static assert(Typedef!int.init == int.init);
6607760c2415Smrg
6608760c2415Smrg Typedef!(float, 1.0) z; // specifies the init
6609760c2415Smrg assert(z == 1.0);
6610760c2415Smrg
6611760c2415Smrg static assert(typeof(z).init == 1.0);
6612760c2415Smrg
6613760c2415Smrg alias Dollar = Typedef!(int, 0, "dollar");
6614760c2415Smrg alias Yen = Typedef!(int, 0, "yen");
6615760c2415Smrg static assert(!is(Dollar == Yen));
6616760c2415Smrg
6617760c2415Smrg Typedef!(int[3]) sa;
6618760c2415Smrg static assert(sa.length == 3);
6619760c2415Smrg static assert(typeof(sa).length == 3);
6620760c2415Smrg
6621760c2415Smrg Typedef!(int[3]) dollar1;
6622760c2415Smrg assert(dollar1[0..$] is dollar1[0 .. 3]);
6623760c2415Smrg
6624760c2415Smrg Typedef!(int[]) dollar2;
6625760c2415Smrg dollar2.length = 3;
6626760c2415Smrg assert(dollar2[0..$] is dollar2[0 .. 3]);
6627760c2415Smrg
6628760c2415Smrg static struct Dollar1
6629760c2415Smrg {
6630760c2415Smrg static struct DollarToken {}
6631760c2415Smrg enum opDollar = DollarToken.init;
6632760c2415Smrg auto opSlice(size_t, DollarToken) { return 1; }
6633760c2415Smrg auto opSlice(size_t, size_t) { return 2; }
6634760c2415Smrg }
6635760c2415Smrg
6636760c2415Smrg Typedef!Dollar1 drange1;
6637760c2415Smrg assert(drange1[0..$] == 1);
6638760c2415Smrg assert(drange1[0 .. 1] == 2);
6639760c2415Smrg
6640760c2415Smrg static struct Dollar2
6641760c2415Smrg {
6642760c2415Smrg size_t opDollar(size_t pos)() { return pos == 0 ? 1 : 100; }
6643760c2415Smrg size_t opIndex(size_t i, size_t j) { return i + j; }
6644760c2415Smrg }
6645760c2415Smrg
6646760c2415Smrg Typedef!Dollar2 drange2;
6647760c2415Smrg assert(drange2[$, $] == 101);
6648760c2415Smrg
6649760c2415Smrg static struct Dollar3
6650760c2415Smrg {
6651760c2415Smrg size_t opDollar() { return 123; }
6652760c2415Smrg size_t opIndex(size_t i) { return i; }
6653760c2415Smrg }
6654760c2415Smrg
6655760c2415Smrg Typedef!Dollar3 drange3;
6656760c2415Smrg assert(drange3[$] == 123);
6657760c2415Smrg }
6658760c2415Smrg
6659760c2415Smrg @safe @nogc pure nothrow unittest // Bugzilla 11703
6660760c2415Smrg {
6661760c2415Smrg alias I = Typedef!int;
6662760c2415Smrg static assert(is(typeof(I.min) == I));
6663760c2415Smrg static assert(is(typeof(I.max) == I));
6664760c2415Smrg
6665760c2415Smrg alias F = Typedef!double;
6666760c2415Smrg static assert(is(typeof(F.infinity) == F));
6667760c2415Smrg static assert(is(typeof(F.epsilon) == F));
6668760c2415Smrg
6669760c2415Smrg F f;
6670760c2415Smrg assert(!is(typeof(F.re).stringof == double));
6671760c2415Smrg assert(!is(typeof(F.im).stringof == double));
6672760c2415Smrg }
6673760c2415Smrg
6674760c2415Smrg @safe unittest
6675760c2415Smrg {
6676760c2415Smrg // bug8655
6677760c2415Smrg import std.typecons;
6678760c2415Smrg import std.bitmanip;
6679760c2415Smrg static import core.stdc.config;
6680760c2415Smrg
6681760c2415Smrg alias c_ulong = Typedef!(core.stdc.config.c_ulong);
6682760c2415Smrg
6683760c2415Smrg static struct Foo
6684760c2415Smrg {
6685760c2415Smrg mixin(bitfields!(
6686760c2415Smrg c_ulong, "NameOffset", 31,
6687760c2415Smrg c_ulong, "NameIsString", 1
6688760c2415Smrg ));
6689760c2415Smrg }
6690760c2415Smrg }
6691760c2415Smrg
6692760c2415Smrg @safe unittest // Issue 12596
6693760c2415Smrg {
6694760c2415Smrg import std.typecons;
6695760c2415Smrg alias TD = Typedef!int;
6696760c2415Smrg TD x = TD(1);
6697760c2415Smrg TD y = TD(x);
6698760c2415Smrg assert(x == y);
6699760c2415Smrg }
6700760c2415Smrg
6701760c2415Smrg @safe unittest // about toHash
6702760c2415Smrg {
6703760c2415Smrg import std.typecons;
6704760c2415Smrg {
6705760c2415Smrg alias TD = Typedef!int;
6706760c2415Smrg int[TD] td;
6707760c2415Smrg td[TD(1)] = 1;
6708760c2415Smrg assert(td[TD(1)] == 1);
6709760c2415Smrg }
6710760c2415Smrg
6711760c2415Smrg {
6712760c2415Smrg alias TD = Typedef!(int[]);
6713760c2415Smrg int[TD] td;
6714760c2415Smrg td[TD([1,2,3,4])] = 2;
6715760c2415Smrg assert(td[TD([1,2,3,4])] == 2);
6716760c2415Smrg }
6717760c2415Smrg
6718760c2415Smrg {
6719760c2415Smrg alias TD = Typedef!(int[][]);
6720760c2415Smrg int[TD] td;
6721760c2415Smrg td[TD([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]])] = 3;
6722760c2415Smrg assert(td[TD([[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]])] == 3);
6723760c2415Smrg }
6724760c2415Smrg
6725760c2415Smrg {
6726760c2415Smrg struct MyStruct{ int x; }
6727760c2415Smrg alias TD = Typedef!MyStruct;
6728760c2415Smrg int[TD] td;
6729760c2415Smrg td[TD(MyStruct(10))] = 4;
6730760c2415Smrg assert(TD(MyStruct(20)) !in td);
6731760c2415Smrg assert(td[TD(MyStruct(10))] == 4);
6732760c2415Smrg }
6733760c2415Smrg
6734760c2415Smrg {
6735760c2415Smrg static struct MyStruct2
6736760c2415Smrg {
6737760c2415Smrg int x;
6738760c2415Smrg size_t toHash() const nothrow @safe { return x; }
6739760c2415Smrg bool opEquals(ref const MyStruct2 r) const { return r.x == x; }
6740760c2415Smrg }
6741760c2415Smrg
6742760c2415Smrg alias TD = Typedef!MyStruct2;
6743760c2415Smrg int[TD] td;
6744760c2415Smrg td[TD(MyStruct2(50))] = 5;
6745760c2415Smrg assert(td[TD(MyStruct2(50))] == 5);
6746760c2415Smrg }
6747760c2415Smrg
6748760c2415Smrg {
6749760c2415Smrg class MyClass{}
6750760c2415Smrg alias TD = Typedef!MyClass;
6751760c2415Smrg int[TD] td;
6752760c2415Smrg auto c = new MyClass;
6753760c2415Smrg td[TD(c)] = 6;
6754760c2415Smrg assert(TD(new MyClass) !in td);
6755760c2415Smrg assert(td[TD(c)] == 6);
6756760c2415Smrg }
6757760c2415Smrg }
6758760c2415Smrg
6759760c2415Smrg @system unittest
6760760c2415Smrg {
6761760c2415Smrg alias String = Typedef!(char[]);
6762760c2415Smrg alias CString = Typedef!(const(char)[]);
6763760c2415Smrg CString cs = "fubar";
6764760c2415Smrg String s = cast(String) cs;
6765760c2415Smrg assert(cs == s);
6766760c2415Smrg char[] s2 = cast(char[]) cs;
6767760c2415Smrg const(char)[] cs2 = cast(const(char)[])s;
6768760c2415Smrg assert(s2 == cs2);
6769760c2415Smrg }
6770760c2415Smrg
6771760c2415Smrg /**
6772760c2415Smrg Allocates a $(D class) object right inside the current scope,
6773760c2415Smrg therefore avoiding the overhead of $(D new). This facility is unsafe;
6774760c2415Smrg it is the responsibility of the user to not escape a reference to the
6775760c2415Smrg object outside the scope.
6776760c2415Smrg
6777760c2415Smrg The class destructor will be called when the result of `scoped()` is
6778760c2415Smrg itself destroyed.
6779760c2415Smrg
6780760c2415Smrg Scoped class instances can be embedded in a parent `class` or `struct`,
6781760c2415Smrg just like a child struct instance. Scoped member variables must have
6782760c2415Smrg type `typeof(scoped!Class(args))`, and be initialized with a call to
6783760c2415Smrg scoped. See below for an example.
6784760c2415Smrg
6785760c2415Smrg Note:
6786760c2415Smrg It's illegal to move a class instance even if you are sure there
6787760c2415Smrg are no pointers to it. As such, it is illegal to move a scoped object.
6788760c2415Smrg */
6789760c2415Smrg template scoped(T)
6790760c2415Smrg if (is(T == class))
6791760c2415Smrg {
6792760c2415Smrg // _d_newclass now use default GC alignment (looks like (void*).sizeof * 2 for
6793760c2415Smrg // small objects). We will just use the maximum of filed alignments.
6794760c2415Smrg alias alignment = classInstanceAlignment!T;
6795760c2415Smrg alias aligned = _alignUp!alignment;
6796760c2415Smrg
6797760c2415Smrg static struct Scoped
6798760c2415Smrg {
6799760c2415Smrg // Addition of `alignment` is required as `Scoped_store` can be misaligned in memory.
6800760c2415Smrg private void[aligned(__traits(classInstanceSize, T) + size_t.sizeof) + alignment] Scoped_store = void;
6801760c2415Smrg
6802760c2415Smrg @property inout(T) Scoped_payload() inout
6803760c2415Smrg {
6804760c2415Smrg void* alignedStore = cast(void*) aligned(cast(uintptr_t) Scoped_store.ptr);
6805760c2415Smrg // As `Scoped` can be unaligned moved in memory class instance should be moved accordingly.
6806760c2415Smrg immutable size_t d = alignedStore - Scoped_store.ptr;
6807760c2415Smrg size_t* currD = cast(size_t*) &Scoped_store[$ - size_t.sizeof];
6808760c2415Smrg if (d != *currD)
6809760c2415Smrg {
6810760c2415Smrg import core.stdc.string : memmove;
6811760c2415Smrg memmove(alignedStore, Scoped_store.ptr + *currD, __traits(classInstanceSize, T));
6812760c2415Smrg *currD = d;
6813760c2415Smrg }
6814760c2415Smrg return cast(inout(T)) alignedStore;
6815760c2415Smrg }
6816760c2415Smrg alias Scoped_payload this;
6817760c2415Smrg
6818760c2415Smrg @disable this();
6819760c2415Smrg @disable this(this);
6820760c2415Smrg
6821760c2415Smrg ~this()
6822760c2415Smrg {
6823760c2415Smrg // `destroy` will also write .init but we have no functions in druntime
6824760c2415Smrg // for deterministic finalization and memory releasing for now.
6825760c2415Smrg .destroy(Scoped_payload);
6826760c2415Smrg }
6827760c2415Smrg }
6828760c2415Smrg
6829760c2415Smrg /** Returns the _scoped object.
6830760c2415Smrg Params: args = Arguments to pass to $(D T)'s constructor.
6831760c2415Smrg */
6832760c2415Smrg @system auto scoped(Args...)(auto ref Args args)
6833760c2415Smrg {
6834760c2415Smrg import std.conv : emplace;
6835760c2415Smrg
6836760c2415Smrg Scoped result = void;
6837760c2415Smrg void* alignedStore = cast(void*) aligned(cast(uintptr_t) result.Scoped_store.ptr);
6838760c2415Smrg immutable size_t d = alignedStore - result.Scoped_store.ptr;
6839760c2415Smrg *cast(size_t*) &result.Scoped_store[$ - size_t.sizeof] = d;
6840760c2415Smrg emplace!(Unqual!T)(result.Scoped_store[d .. $ - size_t.sizeof], args);
6841760c2415Smrg return result;
6842760c2415Smrg }
6843760c2415Smrg }
6844760c2415Smrg
6845760c2415Smrg ///
6846760c2415Smrg @system unittest
6847760c2415Smrg {
6848760c2415Smrg class A
6849760c2415Smrg {
6850760c2415Smrg int x;
6851760c2415Smrg this() {x = 0;}
6852760c2415Smrg this(int i){x = i;}
6853760c2415Smrg ~this() {}
6854760c2415Smrg }
6855760c2415Smrg
6856760c2415Smrg // Standard usage, constructing A on the stack
6857760c2415Smrg auto a1 = scoped!A();
6858760c2415Smrg a1.x = 42;
6859760c2415Smrg
6860760c2415Smrg // Result of `scoped` call implicitly converts to a class reference
6861760c2415Smrg A aRef = a1;
6862760c2415Smrg assert(aRef.x == 42);
6863760c2415Smrg
6864760c2415Smrg // Scoped destruction
6865760c2415Smrg {
6866760c2415Smrg auto a2 = scoped!A(1);
6867760c2415Smrg assert(a2.x == 1);
6868760c2415Smrg aRef = a2;
6869760c2415Smrg // a2 is destroyed here, calling A's destructor
6870760c2415Smrg }
6871760c2415Smrg // aRef is now an invalid reference
6872760c2415Smrg
6873760c2415Smrg // Here the temporary scoped A is immediately destroyed.
6874760c2415Smrg // This means the reference is then invalid.
6875760c2415Smrg version (Bug)
6876760c2415Smrg {
6877760c2415Smrg // Wrong, should use `auto`
6878760c2415Smrg A invalid = scoped!A();
6879760c2415Smrg }
6880760c2415Smrg
6881760c2415Smrg // Restrictions
6882760c2415Smrg version (Bug)
6883760c2415Smrg {
6884760c2415Smrg import std.algorithm.mutation : move;
6885760c2415Smrg auto invalid = a1.move; // illegal, scoped objects can't be moved
6886760c2415Smrg }
6887760c2415Smrg static assert(!is(typeof({
6888760c2415Smrg auto e1 = a1; // illegal, scoped objects can't be copied
6889760c2415Smrg assert([a1][0].x == 42); // ditto
6890760c2415Smrg })));
6891760c2415Smrg static assert(!is(typeof({
6892760c2415Smrg alias ScopedObject = typeof(a1);
6893760c2415Smrg auto e2 = ScopedObject(); // illegal, must be built via scoped!A
6894760c2415Smrg auto e3 = ScopedObject(1); // ditto
6895760c2415Smrg })));
6896760c2415Smrg
6897760c2415Smrg // Use with alias
6898760c2415Smrg alias makeScopedA = scoped!A;
6899760c2415Smrg auto a3 = makeScopedA();
6900760c2415Smrg auto a4 = makeScopedA(1);
6901760c2415Smrg
6902760c2415Smrg // Use as member variable
6903760c2415Smrg struct B
6904760c2415Smrg {
6905760c2415Smrg typeof(scoped!A()) a; // note the trailing parentheses
6906760c2415Smrg
6907760c2415Smrg this(int i)
6908760c2415Smrg {
6909760c2415Smrg // construct member
6910760c2415Smrg a = scoped!A(i);
6911760c2415Smrg }
6912760c2415Smrg }
6913760c2415Smrg
6914760c2415Smrg // Stack-allocate
6915760c2415Smrg auto b1 = B(5);
6916760c2415Smrg aRef = b1.a;
6917760c2415Smrg assert(aRef.x == 5);
6918760c2415Smrg destroy(b1); // calls A's destructor for b1.a
6919760c2415Smrg // aRef is now an invalid reference
6920760c2415Smrg
6921760c2415Smrg // Heap-allocate
6922760c2415Smrg auto b2 = new B(6);
6923760c2415Smrg assert(b2.a.x == 6);
6924760c2415Smrg destroy(*b2); // calls A's destructor for b2.a
6925760c2415Smrg }
6926760c2415Smrg
6927760c2415Smrg private uintptr_t _alignUp(uintptr_t alignment)(uintptr_t n)
6928760c2415Smrg if (alignment > 0 && !((alignment - 1) & alignment))
6929760c2415Smrg {
6930760c2415Smrg enum badEnd = alignment - 1; // 0b11, 0b111, ...
6931760c2415Smrg return (n + badEnd) & ~badEnd;
6932760c2415Smrg }
6933760c2415Smrg
6934760c2415Smrg @system unittest // Issue 6580 testcase
6935760c2415Smrg {
6936760c2415Smrg enum alignment = (void*).alignof;
6937760c2415Smrg
6938760c2415Smrg static class C0 { }
6939760c2415Smrg static class C1 { byte b; }
6940760c2415Smrg static class C2 { byte[2] b; }
6941760c2415Smrg static class C3 { byte[3] b; }
6942760c2415Smrg static class C7 { byte[7] b; }
6943760c2415Smrg static assert(scoped!C0().sizeof % alignment == 0);
6944760c2415Smrg static assert(scoped!C1().sizeof % alignment == 0);
6945760c2415Smrg static assert(scoped!C2().sizeof % alignment == 0);
6946760c2415Smrg static assert(scoped!C3().sizeof % alignment == 0);
6947760c2415Smrg static assert(scoped!C7().sizeof % alignment == 0);
6948760c2415Smrg
6949760c2415Smrg enum longAlignment = long.alignof;
6950760c2415Smrg static class C1long
6951760c2415Smrg {
6952760c2415Smrg long long_; byte byte_ = 4;
6953760c2415Smrg this() { }
6954760c2415Smrg this(long _long, ref int i) { long_ = _long; ++i; }
6955760c2415Smrg }
6956760c2415Smrg static class C2long { byte[2] byte_ = [5, 6]; long long_ = 7; }
6957760c2415Smrg static assert(scoped!C1long().sizeof % longAlignment == 0);
6958760c2415Smrg static assert(scoped!C2long().sizeof % longAlignment == 0);
6959760c2415Smrg
6960760c2415Smrg void alignmentTest()
6961760c2415Smrg {
6962760c2415Smrg int var = 5;
6963760c2415Smrg auto c1long = scoped!C1long(3, var);
6964760c2415Smrg assert(var == 6);
6965760c2415Smrg auto c2long = scoped!C2long();
6966760c2415Smrg assert(cast(uint)&c1long.long_ % longAlignment == 0);
6967760c2415Smrg assert(cast(uint)&c2long.long_ % longAlignment == 0);
6968760c2415Smrg assert(c1long.long_ == 3 && c1long.byte_ == 4);
6969760c2415Smrg assert(c2long.byte_ == [5, 6] && c2long.long_ == 7);
6970760c2415Smrg }
6971760c2415Smrg
6972760c2415Smrg alignmentTest();
6973760c2415Smrg
6974760c2415Smrg version (DigitalMars)
6975760c2415Smrg {
6976760c2415Smrg void test(size_t size)
6977760c2415Smrg {
6978760c2415Smrg import core.stdc.stdlib;
6979760c2415Smrg alloca(size);
6980760c2415Smrg alignmentTest();
6981760c2415Smrg }
6982760c2415Smrg foreach (i; 0 .. 10)
6983760c2415Smrg test(i);
6984760c2415Smrg }
6985760c2415Smrg else
6986760c2415Smrg {
6987760c2415Smrg void test(size_t size)()
6988760c2415Smrg {
6989760c2415Smrg byte[size] arr;
6990760c2415Smrg alignmentTest();
6991760c2415Smrg }
6992760c2415Smrg foreach (i; AliasSeq!(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10))
6993760c2415Smrg test!i();
6994760c2415Smrg }
6995760c2415Smrg }
6996760c2415Smrg
6997760c2415Smrg @system unittest // Original Issue 6580 testcase
6998760c2415Smrg {
6999760c2415Smrg class C { int i; byte b; }
7000760c2415Smrg
7001760c2415Smrg auto sa = [scoped!C(), scoped!C()];
7002760c2415Smrg assert(cast(uint)&sa[0].i % int.alignof == 0);
7003760c2415Smrg assert(cast(uint)&sa[1].i % int.alignof == 0); // fails
7004760c2415Smrg }
7005760c2415Smrg
7006760c2415Smrg @system unittest
7007760c2415Smrg {
7008760c2415Smrg class A { int x = 1; }
7009760c2415Smrg auto a1 = scoped!A();
7010760c2415Smrg assert(a1.x == 1);
7011760c2415Smrg auto a2 = scoped!A();
7012760c2415Smrg a1.x = 42;
7013760c2415Smrg a2.x = 53;
7014760c2415Smrg assert(a1.x == 42);
7015760c2415Smrg }
7016760c2415Smrg
7017760c2415Smrg @system unittest
7018760c2415Smrg {
7019760c2415Smrg class A { int x = 1; this() { x = 2; } }
7020760c2415Smrg auto a1 = scoped!A();
7021760c2415Smrg assert(a1.x == 2);
7022760c2415Smrg auto a2 = scoped!A();
7023760c2415Smrg a1.x = 42;
7024760c2415Smrg a2.x = 53;
7025760c2415Smrg assert(a1.x == 42);
7026760c2415Smrg }
7027760c2415Smrg
7028760c2415Smrg @system unittest
7029760c2415Smrg {
7030760c2415Smrg class A { int x = 1; this(int y) { x = y; } ~this() {} }
7031760c2415Smrg auto a1 = scoped!A(5);
7032760c2415Smrg assert(a1.x == 5);
7033760c2415Smrg auto a2 = scoped!A(42);
7034760c2415Smrg a1.x = 42;
7035760c2415Smrg a2.x = 53;
7036760c2415Smrg assert(a1.x == 42);
7037760c2415Smrg }
7038760c2415Smrg
7039760c2415Smrg @system unittest
7040760c2415Smrg {
7041760c2415Smrg class A { static bool dead; ~this() { dead = true; } }
7042760c2415Smrg class B : A { static bool dead; ~this() { dead = true; } }
7043760c2415Smrg {
7044760c2415Smrg auto b = scoped!B();
7045760c2415Smrg }
7046760c2415Smrg assert(B.dead, "asdasd");
7047760c2415Smrg assert(A.dead, "asdasd");
7048760c2415Smrg }
7049760c2415Smrg
7050760c2415Smrg @system unittest // Issue 8039 testcase
7051760c2415Smrg {
7052760c2415Smrg static int dels;
7053760c2415Smrg static struct S { ~this(){ ++dels; } }
7054760c2415Smrg
7055760c2415Smrg static class A { S s; }
7056760c2415Smrg dels = 0; { scoped!A(); }
7057760c2415Smrg assert(dels == 1);
7058760c2415Smrg
7059760c2415Smrg static class B { S[2] s; }
7060760c2415Smrg dels = 0; { scoped!B(); }
7061760c2415Smrg assert(dels == 2);
7062760c2415Smrg
7063760c2415Smrg static struct S2 { S[3] s; }
7064760c2415Smrg static class C { S2[2] s; }
7065760c2415Smrg dels = 0; { scoped!C(); }
7066760c2415Smrg assert(dels == 6);
7067760c2415Smrg
7068760c2415Smrg static class D: A { S2[2] s; }
7069760c2415Smrg dels = 0; { scoped!D(); }
7070760c2415Smrg assert(dels == 1+6);
7071760c2415Smrg }
7072760c2415Smrg
7073760c2415Smrg @system unittest
7074760c2415Smrg {
7075760c2415Smrg // bug4500
7076760c2415Smrg class A
7077760c2415Smrg {
7078760c2415Smrg this() { a = this; }
7079760c2415Smrg this(int i) { a = this; }
7080760c2415Smrg A a;
7081760c2415Smrg bool check() { return this is a; }
7082760c2415Smrg }
7083760c2415Smrg
7084760c2415Smrg auto a1 = scoped!A();
7085760c2415Smrg assert(a1.check());
7086760c2415Smrg
7087760c2415Smrg auto a2 = scoped!A(1);
7088760c2415Smrg assert(a2.check());
7089760c2415Smrg
7090760c2415Smrg a1.a = a1;
7091760c2415Smrg assert(a1.check());
7092760c2415Smrg }
7093760c2415Smrg
7094760c2415Smrg @system unittest
7095760c2415Smrg {
7096760c2415Smrg static class A
7097760c2415Smrg {
7098760c2415Smrg static int sdtor;
7099760c2415Smrg
7100760c2415Smrg this() { ++sdtor; assert(sdtor == 1); }
7101760c2415Smrg ~this() { assert(sdtor == 1); --sdtor; }
7102760c2415Smrg }
7103760c2415Smrg
7104760c2415Smrg interface Bob {}
7105760c2415Smrg
7106760c2415Smrg static class ABob : A, Bob
7107760c2415Smrg {
7108760c2415Smrg this() { ++sdtor; assert(sdtor == 2); }
7109760c2415Smrg ~this() { assert(sdtor == 2); --sdtor; }
7110760c2415Smrg }
7111760c2415Smrg
7112760c2415Smrg A.sdtor = 0;
7113760c2415Smrg scope(exit) assert(A.sdtor == 0);
7114760c2415Smrg auto abob = scoped!ABob();
7115760c2415Smrg }
7116760c2415Smrg
7117760c2415Smrg @safe unittest
7118760c2415Smrg {
7119760c2415Smrg static class A { this(int) {} }
7120760c2415Smrg static assert(!__traits(compiles, scoped!A()));
7121760c2415Smrg }
7122760c2415Smrg
7123760c2415Smrg @system unittest
7124760c2415Smrg {
7125760c2415Smrg static class A { @property inout(int) foo() inout { return 1; } }
7126760c2415Smrg
7127760c2415Smrg auto a1 = scoped!A();
7128760c2415Smrg assert(a1.foo == 1);
7129760c2415Smrg static assert(is(typeof(a1.foo) == int));
7130760c2415Smrg
7131760c2415Smrg auto a2 = scoped!(const(A))();
7132760c2415Smrg assert(a2.foo == 1);
7133760c2415Smrg static assert(is(typeof(a2.foo) == const(int)));
7134760c2415Smrg
7135760c2415Smrg auto a3 = scoped!(immutable(A))();
7136760c2415Smrg assert(a3.foo == 1);
7137760c2415Smrg static assert(is(typeof(a3.foo) == immutable(int)));
7138760c2415Smrg
7139760c2415Smrg const c1 = scoped!A();
7140760c2415Smrg assert(c1.foo == 1);
7141760c2415Smrg static assert(is(typeof(c1.foo) == const(int)));
7142760c2415Smrg
7143760c2415Smrg const c2 = scoped!(const(A))();
7144760c2415Smrg assert(c2.foo == 1);
7145760c2415Smrg static assert(is(typeof(c2.foo) == const(int)));
7146760c2415Smrg
7147760c2415Smrg const c3 = scoped!(immutable(A))();
7148760c2415Smrg assert(c3.foo == 1);
7149760c2415Smrg static assert(is(typeof(c3.foo) == immutable(int)));
7150760c2415Smrg }
7151760c2415Smrg
7152760c2415Smrg @system unittest
7153760c2415Smrg {
7154760c2415Smrg class C { this(ref int val) { assert(val == 3); ++val; } }
7155760c2415Smrg
7156760c2415Smrg int val = 3;
7157760c2415Smrg auto s = scoped!C(val);
7158760c2415Smrg assert(val == 4);
7159760c2415Smrg }
7160760c2415Smrg
7161760c2415Smrg @system unittest
7162760c2415Smrg {
7163760c2415Smrg class C
7164760c2415Smrg {
7165760c2415Smrg this(){}
7166760c2415Smrg this(int){}
7167760c2415Smrg this(int, int){}
7168760c2415Smrg }
7169760c2415Smrg alias makeScopedC = scoped!C;
7170760c2415Smrg
7171760c2415Smrg auto a = makeScopedC();
7172760c2415Smrg auto b = makeScopedC(1);
7173760c2415Smrg auto c = makeScopedC(1, 1);
7174760c2415Smrg
7175760c2415Smrg static assert(is(typeof(a) == typeof(b)));
7176760c2415Smrg static assert(is(typeof(b) == typeof(c)));
7177760c2415Smrg }
7178760c2415Smrg
7179760c2415Smrg /**
7180760c2415Smrg Defines a simple, self-documenting yes/no flag. This makes it easy for
7181760c2415Smrg APIs to define functions accepting flags without resorting to $(D
7182760c2415Smrg bool), which is opaque in calls, and without needing to define an
7183760c2415Smrg enumerated type separately. Using $(D Flag!"Name") instead of $(D
7184760c2415Smrg bool) makes the flag's meaning visible in calls. Each yes/no flag has
7185760c2415Smrg its own type, which makes confusions and mix-ups impossible.
7186760c2415Smrg
7187760c2415Smrg Example:
7188760c2415Smrg
7189760c2415Smrg Code calling $(D getLine) (usually far away from its definition) can't be
7190760c2415Smrg understood without looking at the documentation, even by users familiar with
7191760c2415Smrg the API:
7192760c2415Smrg ----
7193760c2415Smrg string getLine(bool keepTerminator)
7194760c2415Smrg {
7195760c2415Smrg ...
7196760c2415Smrg if (keepTerminator) ...
7197760c2415Smrg ...
7198760c2415Smrg }
7199760c2415Smrg ...
7200760c2415Smrg auto line = getLine(false);
7201760c2415Smrg ----
7202760c2415Smrg
7203760c2415Smrg Assuming the reverse meaning (i.e. "ignoreTerminator") and inserting the wrong
7204760c2415Smrg code compiles and runs with erroneous results.
7205760c2415Smrg
7206760c2415Smrg After replacing the boolean parameter with an instantiation of $(D Flag), code
7207760c2415Smrg calling $(D getLine) can be easily read and understood even by people not
7208760c2415Smrg fluent with the API:
7209760c2415Smrg
7210760c2415Smrg ----
7211760c2415Smrg string getLine(Flag!"keepTerminator" keepTerminator)
7212760c2415Smrg {
7213760c2415Smrg ...
7214760c2415Smrg if (keepTerminator) ...
7215760c2415Smrg ...
7216760c2415Smrg }
7217760c2415Smrg ...
7218760c2415Smrg auto line = getLine(Yes.keepTerminator);
7219760c2415Smrg ----
7220760c2415Smrg
7221760c2415Smrg The structs $(D Yes) and $(D No) are provided as shorthand for
7222760c2415Smrg $(D Flag!"Name".yes) and $(D Flag!"Name".no) and are preferred for brevity and
7223760c2415Smrg readability. These convenience structs mean it is usually unnecessary and
7224760c2415Smrg counterproductive to create an alias of a $(D Flag) as a way of avoiding typing
7225760c2415Smrg out the full type while specifying the affirmative or negative options.
7226760c2415Smrg
7227760c2415Smrg Passing categorical data by means of unstructured $(D bool)
7228760c2415Smrg parameters is classified under "simple-data coupling" by Steve
7229760c2415Smrg McConnell in the $(LUCKY Code Complete) book, along with three other
7230760c2415Smrg kinds of coupling. The author argues citing several studies that
7231760c2415Smrg coupling has a negative effect on code quality. $(D Flag) offers a
7232760c2415Smrg simple structuring method for passing yes/no flags to APIs.
7233760c2415Smrg */
7234760c2415Smrg template Flag(string name) {
7235760c2415Smrg ///
7236760c2415Smrg enum Flag : bool
7237760c2415Smrg {
7238760c2415Smrg /**
7239760c2415Smrg When creating a value of type $(D Flag!"Name"), use $(D
7240760c2415Smrg Flag!"Name".no) for the negative option. When using a value
7241760c2415Smrg of type $(D Flag!"Name"), compare it against $(D
7242760c2415Smrg Flag!"Name".no) or just $(D false) or $(D 0). */
7243760c2415Smrg no = false,
7244760c2415Smrg
7245760c2415Smrg /** When creating a value of type $(D Flag!"Name"), use $(D
7246760c2415Smrg Flag!"Name".yes) for the affirmative option. When using a
7247760c2415Smrg value of type $(D Flag!"Name"), compare it against $(D
7248760c2415Smrg Flag!"Name".yes).
7249760c2415Smrg */
7250760c2415Smrg yes = true
7251760c2415Smrg }
7252760c2415Smrg }
7253760c2415Smrg
7254760c2415Smrg /**
7255760c2415Smrg Convenience names that allow using e.g. $(D Yes.encryption) instead of
7256760c2415Smrg $(D Flag!"encryption".yes) and $(D No.encryption) instead of $(D
7257760c2415Smrg Flag!"encryption".no).
7258760c2415Smrg */
7259760c2415Smrg struct Yes
7260760c2415Smrg {
7261760c2415Smrg template opDispatch(string name)
7262760c2415Smrg {
7263760c2415Smrg enum opDispatch = Flag!name.yes;
7264760c2415Smrg }
7265760c2415Smrg }
7266760c2415Smrg //template yes(string name) { enum Flag!name yes = Flag!name.yes; }
7267760c2415Smrg
7268760c2415Smrg /// Ditto
7269760c2415Smrg struct No
7270760c2415Smrg {
7271760c2415Smrg template opDispatch(string name)
7272760c2415Smrg {
7273760c2415Smrg enum opDispatch = Flag!name.no;
7274760c2415Smrg }
7275760c2415Smrg }
7276760c2415Smrg
7277760c2415Smrg ///
7278760c2415Smrg @safe unittest
7279760c2415Smrg {
7280760c2415Smrg Flag!"abc" flag1;
7281760c2415Smrg assert(flag1 == Flag!"abc".no);
7282760c2415Smrg assert(flag1 == No.abc);
7283760c2415Smrg assert(!flag1);
7284760c2415Smrg if (flag1) assert(false);
7285760c2415Smrg flag1 = Yes.abc;
7286760c2415Smrg assert(flag1);
7287760c2415Smrg if (!flag1) assert(false);
7288760c2415Smrg if (flag1) {} else assert(false);
7289760c2415Smrg assert(flag1 == Yes.abc);
7290760c2415Smrg }
7291760c2415Smrg
7292760c2415Smrg /**
7293760c2415Smrg Detect whether an enum is of integral type and has only "flag" values
7294760c2415Smrg (i.e. values with a bit count of exactly 1).
7295760c2415Smrg Additionally, a zero value is allowed for compatibility with enums including
7296760c2415Smrg a "None" value.
7297760c2415Smrg */
7298760c2415Smrg template isBitFlagEnum(E)
7299760c2415Smrg {
7300760c2415Smrg static if (is(E Base == enum) && isIntegral!Base)
7301760c2415Smrg {
7302760c2415Smrg enum isBitFlagEnum = (E.min >= 0) &&
7303760c2415Smrg {
7304760c2415Smrg foreach (immutable flag; EnumMembers!E)
7305760c2415Smrg {
7306760c2415Smrg Base value = flag;
7307760c2415Smrg value &= value - 1;
7308760c2415Smrg if (value != 0) return false;
7309760c2415Smrg }
7310760c2415Smrg return true;
7311760c2415Smrg }();
7312760c2415Smrg }
7313760c2415Smrg else
7314760c2415Smrg {
7315760c2415Smrg enum isBitFlagEnum = false;
7316760c2415Smrg }
7317760c2415Smrg }
7318760c2415Smrg
7319760c2415Smrg ///
7320760c2415Smrg @safe pure nothrow unittest
7321760c2415Smrg {
7322760c2415Smrg enum A
7323760c2415Smrg {
7324760c2415Smrg None,
7325760c2415Smrg A = 1 << 0,
7326760c2415Smrg B = 1 << 1,
7327760c2415Smrg C = 1 << 2,
7328760c2415Smrg D = 1 << 3,
7329760c2415Smrg }
7330760c2415Smrg
7331760c2415Smrg static assert(isBitFlagEnum!A);
7332760c2415Smrg
7333760c2415Smrg enum B
7334760c2415Smrg {
7335760c2415Smrg A,
7336760c2415Smrg B,
7337760c2415Smrg C,
7338760c2415Smrg D // D == 3
7339760c2415Smrg }
7340760c2415Smrg
7341760c2415Smrg static assert(!isBitFlagEnum!B);
7342760c2415Smrg
7343760c2415Smrg enum C: double
7344760c2415Smrg {
7345760c2415Smrg A = 1 << 0,
7346760c2415Smrg B = 1 << 1
7347760c2415Smrg }
7348760c2415Smrg
7349760c2415Smrg static assert(!isBitFlagEnum!C);
7350760c2415Smrg }
7351760c2415Smrg
7352760c2415Smrg /**
7353760c2415Smrg A typesafe structure for storing combinations of enum values.
7354760c2415Smrg
7355760c2415Smrg This template defines a simple struct to represent bitwise OR combinations of
7356760c2415Smrg enum values. It can be used if all the enum values are integral constants with
7357760c2415Smrg a bit count of at most 1, or if the $(D unsafe) parameter is explicitly set to
7358760c2415Smrg Yes.
7359760c2415Smrg This is much safer than using the enum itself to store
7360760c2415Smrg the OR combination, which can produce surprising effects like this:
7361760c2415Smrg ----
7362760c2415Smrg enum E
7363760c2415Smrg {
7364760c2415Smrg A = 1 << 0,
7365760c2415Smrg B = 1 << 1
7366760c2415Smrg }
7367760c2415Smrg E e = E.A | E.B;
7368760c2415Smrg // will throw SwitchError
7369760c2415Smrg final switch (e)
7370760c2415Smrg {
7371760c2415Smrg case E.A:
7372760c2415Smrg return;
7373760c2415Smrg case E.B:
7374760c2415Smrg return;
7375760c2415Smrg }
7376760c2415Smrg ----
7377760c2415Smrg */
7378760c2415Smrg struct BitFlags(E, Flag!"unsafe" unsafe = No.unsafe) if (unsafe || isBitFlagEnum!(E))
7379760c2415Smrg {
7380760c2415Smrg @safe @nogc pure nothrow:
7381760c2415Smrg private:
7382760c2415Smrg enum isBaseEnumType(T) = is(E == T);
7383760c2415Smrg alias Base = OriginalType!E;
7384760c2415Smrg Base mValue;
7385760c2415Smrg static struct Negation
7386760c2415Smrg {
7387760c2415Smrg @safe @nogc pure nothrow:
7388760c2415Smrg private:
7389760c2415Smrg Base mValue;
7390760c2415Smrg
7391760c2415Smrg // Prevent non-copy construction outside the module.
7392760c2415Smrg @disable this();
7393760c2415Smrg this(Base value)
7394760c2415Smrg {
7395760c2415Smrg mValue = value;
7396760c2415Smrg }
7397760c2415Smrg }
7398760c2415Smrg
7399760c2415Smrg public:
7400760c2415Smrg this(E flag)
7401760c2415Smrg {
7402760c2415Smrg this = flag;
7403760c2415Smrg }
7404760c2415Smrg
7405760c2415Smrg this(T...)(T flags)
7406760c2415Smrg if (allSatisfy!(isBaseEnumType, T))
7407760c2415Smrg {
7408760c2415Smrg this = flags;
7409760c2415Smrg }
7410760c2415Smrg
7411760c2415Smrg bool opCast(B: bool)() const
7412760c2415Smrg {
7413760c2415Smrg return mValue != 0;
7414760c2415Smrg }
7415760c2415Smrg
7416760c2415Smrg Base opCast(B)() const
7417760c2415Smrg if (isImplicitlyConvertible!(Base, B))
7418760c2415Smrg {
7419760c2415Smrg return mValue;
7420760c2415Smrg }
7421760c2415Smrg
7422760c2415Smrg Negation opUnary(string op)() const
7423760c2415Smrg if (op == "~")
7424760c2415Smrg {
7425760c2415Smrg return Negation(~mValue);
7426760c2415Smrg }
7427760c2415Smrg
7428760c2415Smrg auto ref opAssign(T...)(T flags)
7429760c2415Smrg if (allSatisfy!(isBaseEnumType, T))
7430760c2415Smrg {
7431760c2415Smrg mValue = 0;
7432760c2415Smrg foreach (E flag; flags)
7433760c2415Smrg {
7434760c2415Smrg mValue |= flag;
7435760c2415Smrg }
7436760c2415Smrg return this;
7437760c2415Smrg }
7438760c2415Smrg
7439760c2415Smrg auto ref opAssign(E flag)
7440760c2415Smrg {
7441760c2415Smrg mValue = flag;
7442760c2415Smrg return this;
7443760c2415Smrg }
7444760c2415Smrg
7445760c2415Smrg auto ref opOpAssign(string op: "|")(BitFlags flags)
7446760c2415Smrg {
7447760c2415Smrg mValue |= flags.mValue;
7448760c2415Smrg return this;
7449760c2415Smrg }
7450760c2415Smrg
7451760c2415Smrg auto ref opOpAssign(string op: "&")(BitFlags flags)
7452760c2415Smrg {
7453760c2415Smrg mValue &= flags.mValue;
7454760c2415Smrg return this;
7455760c2415Smrg }
7456760c2415Smrg
7457760c2415Smrg auto ref opOpAssign(string op: "|")(E flag)
7458760c2415Smrg {
7459760c2415Smrg mValue |= flag;
7460760c2415Smrg return this;
7461760c2415Smrg }
7462760c2415Smrg
7463760c2415Smrg auto ref opOpAssign(string op: "&")(E flag)
7464760c2415Smrg {
7465760c2415Smrg mValue &= flag;
7466760c2415Smrg return this;
7467760c2415Smrg }
7468760c2415Smrg
7469760c2415Smrg auto ref opOpAssign(string op: "&")(Negation negatedFlags)
7470760c2415Smrg {
7471760c2415Smrg mValue &= negatedFlags.mValue;
7472760c2415Smrg return this;
7473760c2415Smrg }
7474760c2415Smrg
7475760c2415Smrg auto opBinary(string op)(BitFlags flags) const
7476760c2415Smrg if (op == "|" || op == "&")
7477760c2415Smrg {
7478760c2415Smrg BitFlags result = this;
7479760c2415Smrg result.opOpAssign!op(flags);
7480760c2415Smrg return result;
7481760c2415Smrg }
7482760c2415Smrg
7483760c2415Smrg auto opBinary(string op)(E flag) const
7484760c2415Smrg if (op == "|" || op == "&")
7485760c2415Smrg {
7486760c2415Smrg BitFlags result = this;
7487760c2415Smrg result.opOpAssign!op(flag);
7488760c2415Smrg return result;
7489760c2415Smrg }
7490760c2415Smrg
7491760c2415Smrg auto opBinary(string op: "&")(Negation negatedFlags) const
7492760c2415Smrg {
7493760c2415Smrg BitFlags result = this;
7494760c2415Smrg result.opOpAssign!op(negatedFlags);
7495760c2415Smrg return result;
7496760c2415Smrg }
7497760c2415Smrg
7498760c2415Smrg auto opBinaryRight(string op)(E flag) const
7499760c2415Smrg if (op == "|" || op == "&")
7500760c2415Smrg {
7501760c2415Smrg return opBinary!op(flag);
7502760c2415Smrg }
7503760c2415Smrg }
7504760c2415Smrg
7505760c2415Smrg /// BitFlags can be manipulated with the usual operators
7506760c2415Smrg @safe @nogc pure nothrow unittest
7507760c2415Smrg {
7508760c2415Smrg import std.traits : EnumMembers;
7509760c2415Smrg
7510760c2415Smrg // You can use such an enum with BitFlags straight away
7511760c2415Smrg enum Enum
7512760c2415Smrg {
7513760c2415Smrg None,
7514760c2415Smrg A = 1 << 0,
7515760c2415Smrg B = 1 << 1,
7516760c2415Smrg C = 1 << 2
7517760c2415Smrg }
7518760c2415Smrg BitFlags!Enum flags1;
7519760c2415Smrg assert(!(flags1 & (Enum.A | Enum.B | Enum.C)));
7520760c2415Smrg
7521760c2415Smrg // You need to specify the `unsafe` parameter for enum with custom values
7522760c2415Smrg enum UnsafeEnum
7523760c2415Smrg {
7524760c2415Smrg A,
7525760c2415Smrg B,
7526760c2415Smrg C,
7527760c2415Smrg D = B|C
7528760c2415Smrg }
7529760c2415Smrg static assert(!__traits(compiles, { BitFlags!UnsafeEnum flags2; }));
7530760c2415Smrg BitFlags!(UnsafeEnum, Yes.unsafe) flags3;
7531760c2415Smrg
7532760c2415Smrg immutable BitFlags!Enum flags_empty;
7533760c2415Smrg // A default constructed BitFlags has no value set
7534760c2415Smrg assert(!(flags_empty & Enum.A) && !(flags_empty & Enum.B) && !(flags_empty & Enum.C));
7535760c2415Smrg
7536760c2415Smrg // Value can be set with the | operator
7537760c2415Smrg immutable BitFlags!Enum flags_A = flags_empty | Enum.A;
7538760c2415Smrg
7539760c2415Smrg // And tested with the & operator
7540760c2415Smrg assert(flags_A & Enum.A);
7541760c2415Smrg
7542760c2415Smrg // Which commutes
7543760c2415Smrg assert(Enum.A & flags_A);
7544760c2415Smrg
7545760c2415Smrg // BitFlags can be variadically initialized
7546760c2415Smrg immutable BitFlags!Enum flags_AB = BitFlags!Enum(Enum.A, Enum.B);
7547760c2415Smrg assert((flags_AB & Enum.A) && (flags_AB & Enum.B) && !(flags_AB & Enum.C));
7548760c2415Smrg
7549760c2415Smrg // Use the ~ operator for subtracting flags
7550760c2415Smrg immutable BitFlags!Enum flags_B = flags_AB & ~BitFlags!Enum(Enum.A);
7551760c2415Smrg assert(!(flags_B & Enum.A) && (flags_B & Enum.B) && !(flags_B & Enum.C));
7552760c2415Smrg
7553760c2415Smrg // You can use the EnumMembers template to set all flags
7554760c2415Smrg immutable BitFlags!Enum flags_all = EnumMembers!Enum;
7555760c2415Smrg
7556760c2415Smrg // use & between BitFlags for intersection
7557760c2415Smrg immutable BitFlags!Enum flags_BC = BitFlags!Enum(Enum.B, Enum.C);
7558760c2415Smrg assert(flags_B == (flags_BC & flags_AB));
7559760c2415Smrg
7560760c2415Smrg // All the binary operators work in their assignment version
7561760c2415Smrg BitFlags!Enum temp = flags_empty;
7562760c2415Smrg temp |= flags_AB;
7563760c2415Smrg assert(temp == (flags_empty | flags_AB));
7564760c2415Smrg temp = flags_empty;
7565760c2415Smrg temp |= Enum.B;
7566760c2415Smrg assert(temp == (flags_empty | Enum.B));
7567760c2415Smrg temp = flags_empty;
7568760c2415Smrg temp &= flags_AB;
7569760c2415Smrg assert(temp == (flags_empty & flags_AB));
7570760c2415Smrg temp = flags_empty;
7571760c2415Smrg temp &= Enum.A;
7572760c2415Smrg assert(temp == (flags_empty & Enum.A));
7573760c2415Smrg
7574760c2415Smrg // BitFlags with no value set evaluate to false
7575760c2415Smrg assert(!flags_empty);
7576760c2415Smrg
7577760c2415Smrg // BitFlags with at least one value set evaluate to true
7578760c2415Smrg assert(flags_A);
7579760c2415Smrg
7580760c2415Smrg // This can be useful to check intersection between BitFlags
7581760c2415Smrg assert(flags_A & flags_AB);
7582760c2415Smrg assert(flags_AB & Enum.A);
7583760c2415Smrg
7584760c2415Smrg // Finally, you can of course get you raw value out of flags
7585760c2415Smrg auto value = cast(int) flags_A;
7586760c2415Smrg assert(value == Enum.A);
7587760c2415Smrg }
7588760c2415Smrg
7589760c2415Smrg // ReplaceType
7590760c2415Smrg /**
7591760c2415Smrg Replaces all occurrences of `From` into `To`, in one or more types `T`. For
7592760c2415Smrg example, $(D ReplaceType!(int, uint, Tuple!(int, float)[string])) yields
7593760c2415Smrg $(D Tuple!(uint, float)[string]). The types in which replacement is performed
7594760c2415Smrg may be arbitrarily complex, including qualifiers, built-in type constructors
7595760c2415Smrg (pointers, arrays, associative arrays, functions, and delegates), and template
7596760c2415Smrg instantiations; replacement proceeds transitively through the type definition.
7597760c2415Smrg However, member types in `struct`s or `class`es are not replaced because there
7598760c2415Smrg are no ways to express the types resulting after replacement.
7599760c2415Smrg
7600760c2415Smrg This is an advanced type manipulation necessary e.g. for replacing the
7601760c2415Smrg placeholder type `This` in $(REF Algebraic, std,variant).
7602760c2415Smrg
7603760c2415Smrg Returns: `ReplaceType` aliases itself to the type(s) that result after
7604760c2415Smrg replacement.
7605760c2415Smrg */
7606760c2415Smrg template ReplaceType(From, To, T...)
7607760c2415Smrg {
7608760c2415Smrg static if (T.length == 1)
7609760c2415Smrg {
7610760c2415Smrg static if (is(T[0] == From))
7611760c2415Smrg alias ReplaceType = To;
7612760c2415Smrg else static if (is(T[0] == const(U), U))
7613760c2415Smrg alias ReplaceType = const(ReplaceType!(From, To, U));
7614760c2415Smrg else static if (is(T[0] == immutable(U), U))
7615760c2415Smrg alias ReplaceType = immutable(ReplaceType!(From, To, U));
7616760c2415Smrg else static if (is(T[0] == shared(U), U))
7617760c2415Smrg alias ReplaceType = shared(ReplaceType!(From, To, U));
7618760c2415Smrg else static if (is(T[0] == U*, U))
7619760c2415Smrg {
7620760c2415Smrg static if (is(U == function))
7621760c2415Smrg alias ReplaceType = replaceTypeInFunctionType!(From, To, T[0]);
7622760c2415Smrg else
7623760c2415Smrg alias ReplaceType = ReplaceType!(From, To, U)*;
7624760c2415Smrg }
7625760c2415Smrg else static if (is(T[0] == delegate))
7626760c2415Smrg {
7627760c2415Smrg alias ReplaceType = replaceTypeInFunctionType!(From, To, T[0]);
7628760c2415Smrg }
7629760c2415Smrg else static if (is(T[0] == function))
7630760c2415Smrg {
7631760c2415Smrg static assert(0, "Function types not supported," ~
7632760c2415Smrg " use a function pointer type instead of " ~ T[0].stringof);
7633760c2415Smrg }
7634760c2415Smrg else static if (is(T[0] : U!V, alias U, V...))
7635760c2415Smrg {
7636760c2415Smrg template replaceTemplateArgs(T...)
7637760c2415Smrg {
7638760c2415Smrg static if (is(typeof(T[0]))) // template argument is value or symbol
7639760c2415Smrg enum replaceTemplateArgs = T[0];
7640760c2415Smrg else
7641760c2415Smrg alias replaceTemplateArgs = ReplaceType!(From, To, T[0]);
7642760c2415Smrg }
7643760c2415Smrg alias ReplaceType = U!(staticMap!(replaceTemplateArgs, V));
7644760c2415Smrg }
7645760c2415Smrg else static if (is(T[0] == struct))
7646760c2415Smrg // don't match with alias this struct below (Issue 15168)
7647760c2415Smrg alias ReplaceType = T[0];
7648760c2415Smrg else static if (is(T[0] == U[], U))
7649760c2415Smrg alias ReplaceType = ReplaceType!(From, To, U)[];
7650760c2415Smrg else static if (is(T[0] == U[n], U, size_t n))
7651760c2415Smrg alias ReplaceType = ReplaceType!(From, To, U)[n];
7652760c2415Smrg else static if (is(T[0] == U[V], U, V))
7653760c2415Smrg alias ReplaceType =
7654760c2415Smrg ReplaceType!(From, To, U)[ReplaceType!(From, To, V)];
7655760c2415Smrg else
7656760c2415Smrg alias ReplaceType = T[0];
7657760c2415Smrg }
7658760c2415Smrg else static if (T.length > 1)
7659760c2415Smrg {
7660760c2415Smrg alias ReplaceType = AliasSeq!(ReplaceType!(From, To, T[0]),
7661760c2415Smrg ReplaceType!(From, To, T[1 .. $]));
7662760c2415Smrg }
7663760c2415Smrg else
7664760c2415Smrg {
7665760c2415Smrg alias ReplaceType = AliasSeq!();
7666760c2415Smrg }
7667760c2415Smrg }
7668760c2415Smrg
7669760c2415Smrg ///
7670760c2415Smrg @safe unittest
7671760c2415Smrg {
7672760c2415Smrg static assert(
7673760c2415Smrg is(ReplaceType!(int, string, int[]) == string[]) &&
7674760c2415Smrg is(ReplaceType!(int, string, int[int]) == string[string]) &&
7675760c2415Smrg is(ReplaceType!(int, string, const(int)[]) == const(string)[]) &&
7676760c2415Smrg is(ReplaceType!(int, string, Tuple!(int[], float))
7677760c2415Smrg == Tuple!(string[], float))
7678760c2415Smrg );
7679760c2415Smrg }
7680760c2415Smrg
7681760c2415Smrg private template replaceTypeInFunctionType(From, To, fun)
7682760c2415Smrg {
7683760c2415Smrg alias RX = ReplaceType!(From, To, ReturnType!fun);
7684760c2415Smrg alias PX = AliasSeq!(ReplaceType!(From, To, Parameters!fun));
7685760c2415Smrg // Wrapping with AliasSeq is neccesary because ReplaceType doesn't return
7686760c2415Smrg // tuple if Parameters!fun.length == 1
7687760c2415Smrg
7688760c2415Smrg string gen()
7689760c2415Smrg {
7690760c2415Smrg enum linkage = functionLinkage!fun;
7691760c2415Smrg alias attributes = functionAttributes!fun;
7692760c2415Smrg enum variadicStyle = variadicFunctionStyle!fun;
7693760c2415Smrg alias storageClasses = ParameterStorageClassTuple!fun;
7694760c2415Smrg
7695760c2415Smrg string result;
7696760c2415Smrg
7697760c2415Smrg result ~= "extern(" ~ linkage ~ ") ";
7698760c2415Smrg static if (attributes & FunctionAttribute.ref_)
7699760c2415Smrg {
7700760c2415Smrg result ~= "ref ";
7701760c2415Smrg }
7702760c2415Smrg
7703760c2415Smrg result ~= "RX";
7704760c2415Smrg static if (is(fun == delegate))
7705760c2415Smrg result ~= " delegate";
7706760c2415Smrg else
7707760c2415Smrg result ~= " function";
7708760c2415Smrg
7709760c2415Smrg result ~= "(";
7710760c2415Smrg foreach (i, _; PX)
7711760c2415Smrg {
7712760c2415Smrg if (i)
7713760c2415Smrg result ~= ", ";
7714760c2415Smrg if (storageClasses[i] & ParameterStorageClass.scope_)
7715760c2415Smrg result ~= "scope ";
7716760c2415Smrg if (storageClasses[i] & ParameterStorageClass.out_)
7717760c2415Smrg result ~= "out ";
7718760c2415Smrg if (storageClasses[i] & ParameterStorageClass.ref_)
7719760c2415Smrg result ~= "ref ";
7720760c2415Smrg if (storageClasses[i] & ParameterStorageClass.lazy_)
7721760c2415Smrg result ~= "lazy ";
7722760c2415Smrg if (storageClasses[i] & ParameterStorageClass.return_)
7723760c2415Smrg result ~= "return ";
7724760c2415Smrg
7725760c2415Smrg result ~= "PX[" ~ i.stringof ~ "]";
7726760c2415Smrg }
7727760c2415Smrg static if (variadicStyle == Variadic.typesafe)
7728760c2415Smrg result ~= " ...";
7729760c2415Smrg else static if (variadicStyle != Variadic.no)
7730760c2415Smrg result ~= ", ...";
7731760c2415Smrg result ~= ")";
7732760c2415Smrg
7733760c2415Smrg static if (attributes & FunctionAttribute.pure_)
7734760c2415Smrg result ~= " pure";
7735760c2415Smrg static if (attributes & FunctionAttribute.nothrow_)
7736760c2415Smrg result ~= " nothrow";
7737760c2415Smrg static if (attributes & FunctionAttribute.property)
7738760c2415Smrg result ~= " @property";
7739760c2415Smrg static if (attributes & FunctionAttribute.trusted)
7740760c2415Smrg result ~= " @trusted";
7741760c2415Smrg static if (attributes & FunctionAttribute.safe)
7742760c2415Smrg result ~= " @safe";
7743760c2415Smrg static if (attributes & FunctionAttribute.nogc)
7744760c2415Smrg result ~= " @nogc";
7745760c2415Smrg static if (attributes & FunctionAttribute.system)
7746760c2415Smrg result ~= " @system";
7747760c2415Smrg static if (attributes & FunctionAttribute.const_)
7748760c2415Smrg result ~= " const";
7749760c2415Smrg static if (attributes & FunctionAttribute.immutable_)
7750760c2415Smrg result ~= " immutable";
7751760c2415Smrg static if (attributes & FunctionAttribute.inout_)
7752760c2415Smrg result ~= " inout";
7753760c2415Smrg static if (attributes & FunctionAttribute.shared_)
7754760c2415Smrg result ~= " shared";
7755760c2415Smrg static if (attributes & FunctionAttribute.return_)
7756760c2415Smrg result ~= " return";
7757760c2415Smrg
7758760c2415Smrg return result;
7759760c2415Smrg }
7760760c2415Smrg //pragma(msg, "gen ==> ", gen());
7761760c2415Smrg
7762760c2415Smrg mixin("alias replaceTypeInFunctionType = " ~ gen() ~ ";");
7763760c2415Smrg }
7764760c2415Smrg
7765760c2415Smrg @safe unittest
7766760c2415Smrg {
7767760c2415Smrg template Test(Ts...)
7768760c2415Smrg {
7769760c2415Smrg static if (Ts.length)
7770760c2415Smrg {
7771760c2415Smrg //pragma(msg, "Testing: ReplaceType!("~Ts[0].stringof~", "
7772760c2415Smrg // ~Ts[1].stringof~", "~Ts[2].stringof~")");
7773760c2415Smrg static assert(is(ReplaceType!(Ts[0], Ts[1], Ts[2]) == Ts[3]),
7774760c2415Smrg "ReplaceType!("~Ts[0].stringof~", "~Ts[1].stringof~", "
7775760c2415Smrg ~Ts[2].stringof~") == "
7776760c2415Smrg ~ReplaceType!(Ts[0], Ts[1], Ts[2]).stringof);
7777760c2415Smrg alias Test = Test!(Ts[4 .. $]);
7778760c2415Smrg }
7779760c2415Smrg else alias Test = void;
7780760c2415Smrg }
7781760c2415Smrg
7782760c2415Smrg //import core.stdc.stdio;
7783760c2415Smrg alias RefFun1 = ref int function(float, long);
7784760c2415Smrg alias RefFun2 = ref float function(float, long);
7785760c2415Smrg extern(C) int printf(const char*, ...) nothrow @nogc @system;
7786760c2415Smrg extern(C) float floatPrintf(const char*, ...) nothrow @nogc @system;
7787760c2415Smrg int func(float);
7788760c2415Smrg
7789760c2415Smrg int x;
7790760c2415Smrg struct S1 { void foo() { x = 1; } }
7791760c2415Smrg struct S2 { void bar() { x = 2; } }
7792760c2415Smrg
7793760c2415Smrg alias Pass = Test!(
7794760c2415Smrg int, float, typeof(&func), float delegate(float),
7795760c2415Smrg int, float, typeof(&printf), typeof(&floatPrintf),
7796760c2415Smrg int, float, int function(out long, ...),
7797760c2415Smrg float function(out long, ...),
7798760c2415Smrg int, float, int function(ref float, long),
7799760c2415Smrg float function(ref float, long),
7800760c2415Smrg int, float, int function(ref int, long),
7801760c2415Smrg float function(ref float, long),
7802760c2415Smrg int, float, int function(out int, long),
7803760c2415Smrg float function(out float, long),
7804760c2415Smrg int, float, int function(lazy int, long),
7805760c2415Smrg float function(lazy float, long),
7806760c2415Smrg int, float, int function(out long, ref const int),
7807760c2415Smrg float function(out long, ref const float),
7808760c2415Smrg int, int, int, int,
7809760c2415Smrg int, float, int, float,
7810760c2415Smrg int, float, const int, const float,
7811760c2415Smrg int, float, immutable int, immutable float,
7812760c2415Smrg int, float, shared int, shared float,
7813760c2415Smrg int, float, int*, float*,
7814760c2415Smrg int, float, const(int)*, const(float)*,
7815760c2415Smrg int, float, const(int*), const(float*),
7816760c2415Smrg const(int)*, float, const(int*), const(float),
7817760c2415Smrg int*, float, const(int)*, const(int)*,
7818760c2415Smrg int, float, int[], float[],
7819760c2415Smrg int, float, int[42], float[42],
7820760c2415Smrg int, float, const(int)[42], const(float)[42],
7821760c2415Smrg int, float, const(int[42]), const(float[42]),
7822760c2415Smrg int, float, int[int], float[float],
7823760c2415Smrg int, float, int[double], float[double],
7824760c2415Smrg int, float, double[int], double[float],
7825760c2415Smrg int, float, int function(float, long), float function(float, long),
7826760c2415Smrg int, float, int function(float), float function(float),
7827760c2415Smrg int, float, int function(float, int), float function(float, float),
7828760c2415Smrg int, float, int delegate(float, long), float delegate(float, long),
7829760c2415Smrg int, float, int delegate(float), float delegate(float),
7830760c2415Smrg int, float, int delegate(float, int), float delegate(float, float),
7831760c2415Smrg int, float, Unique!int, Unique!float,
7832760c2415Smrg int, float, Tuple!(float, int), Tuple!(float, float),
7833760c2415Smrg int, float, RefFun1, RefFun2,
7834760c2415Smrg S1, S2,
7835760c2415Smrg S1[1][][S1]* function(),
7836760c2415Smrg S2[1][][S2]* function(),
7837760c2415Smrg int, string,
7838760c2415Smrg int[3] function( int[] arr, int[2] ...) pure @trusted,
7839760c2415Smrg string[3] function(string[] arr, string[2] ...) pure @trusted,
7840760c2415Smrg );
7841760c2415Smrg
7842760c2415Smrg // Bugzilla 15168
7843760c2415Smrg static struct T1 { string s; alias s this; }
7844760c2415Smrg static struct T2 { char[10] s; alias s this; }
7845760c2415Smrg static struct T3 { string[string] s; alias s this; }
7846760c2415Smrg alias Pass2 = Test!(
7847760c2415Smrg ubyte, ubyte, T1, T1,
7848760c2415Smrg ubyte, ubyte, T2, T2,
7849760c2415Smrg ubyte, ubyte, T3, T3,
7850760c2415Smrg );
7851760c2415Smrg }
7852760c2415Smrg
7853760c2415Smrg @safe unittest // Bugzilla 17116
7854760c2415Smrg {
7855760c2415Smrg alias ConstDg = void delegate(float) const;
7856760c2415Smrg alias B = void delegate(int) const;
7857760c2415Smrg alias A = ReplaceType!(float, int, ConstDg);
7858760c2415Smrg static assert(is(B == A));
7859760c2415Smrg }
7860760c2415Smrg
7861760c2415Smrg /**
7862760c2415Smrg Ternary type with three truth values:
7863760c2415Smrg
7864760c2415Smrg $(UL
7865760c2415Smrg $(LI `Ternary.yes` for `true`)
7866760c2415Smrg $(LI `Ternary.no` for `false`)
7867760c2415Smrg $(LI `Ternary.unknown` as an unknown state)
7868760c2415Smrg )
7869760c2415Smrg
7870760c2415Smrg Also known as trinary, trivalent, or trilean.
7871760c2415Smrg
7872760c2415Smrg See_Also:
7873760c2415Smrg $(HTTP en.wikipedia.org/wiki/Three-valued_logic,
7874760c2415Smrg Three Valued Logic on Wikipedia)
7875760c2415Smrg */
7876760c2415Smrg struct Ternary
7877760c2415Smrg {
7878760c2415Smrg @safe @nogc nothrow pure:
7879760c2415Smrg
7880760c2415Smrg private ubyte value = 6;
7881760c2415Smrg private static Ternary make(ubyte b)
7882760c2415Smrg {
7883760c2415Smrg Ternary r = void;
7884760c2415Smrg r.value = b;
7885760c2415Smrg return r;
7886760c2415Smrg }
7887760c2415Smrg
7888760c2415Smrg /**
7889760c2415Smrg The possible states of the `Ternary`
7890760c2415Smrg */
7891760c2415Smrg enum no = make(0);
7892760c2415Smrg /// ditto
7893760c2415Smrg enum yes = make(2);
7894760c2415Smrg /// ditto
7895760c2415Smrg enum unknown = make(6);
7896760c2415Smrg
7897760c2415Smrg /**
7898760c2415Smrg Construct and assign from a `bool`, receiving `no` for `false` and `yes`
7899760c2415Smrg for `true`.
7900760c2415Smrg */
7901760c2415Smrg this(bool b) { value = b << 1; }
7902760c2415Smrg
7903760c2415Smrg /// ditto
7904760c2415Smrg void opAssign(bool b) { value = b << 1; }
7905760c2415Smrg
7906760c2415Smrg /**
7907760c2415Smrg Construct a ternary value from another ternary value
7908760c2415Smrg */
7909760c2415Smrg this(const Ternary b) { value = b.value; }
7910760c2415Smrg
7911760c2415Smrg /**
7912760c2415Smrg $(TABLE Truth table for logical operations,
7913760c2415Smrg $(TR $(TH `a`) $(TH `b`) $(TH `$(TILDE)a`) $(TH `a | b`) $(TH `a & b`) $(TH `a ^ b`))
7914760c2415Smrg $(TR $(TD `no`) $(TD `no`) $(TD `yes`) $(TD `no`) $(TD `no`) $(TD `no`))
7915760c2415Smrg $(TR $(TD `no`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `no`) $(TD `yes`))
7916760c2415Smrg $(TR $(TD `no`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `no`) $(TD `unknown`))
7917760c2415Smrg $(TR $(TD `yes`) $(TD `no`) $(TD `no`) $(TD `yes`) $(TD `no`) $(TD `yes`))
7918760c2415Smrg $(TR $(TD `yes`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `yes`) $(TD `no`))
7919760c2415Smrg $(TR $(TD `yes`) $(TD `unknown`) $(TD) $(TD `yes`) $(TD `unknown`) $(TD `unknown`))
7920760c2415Smrg $(TR $(TD `unknown`) $(TD `no`) $(TD `unknown`) $(TD `unknown`) $(TD `no`) $(TD `unknown`))
7921760c2415Smrg $(TR $(TD `unknown`) $(TD `yes`) $(TD) $(TD `yes`) $(TD `unknown`) $(TD `unknown`))
7922760c2415Smrg $(TR $(TD `unknown`) $(TD `unknown`) $(TD) $(TD `unknown`) $(TD `unknown`) $(TD `unknown`))
7923760c2415Smrg )
7924760c2415Smrg */
7925760c2415Smrg Ternary opUnary(string s)() if (s == "~")
7926760c2415Smrg {
7927760c2415Smrg return make((386 >> value) & 6);
7928760c2415Smrg }
7929760c2415Smrg
7930760c2415Smrg /// ditto
7931760c2415Smrg Ternary opBinary(string s)(Ternary rhs) if (s == "|")
7932760c2415Smrg {
7933760c2415Smrg return make((25_512 >> (value + rhs.value)) & 6);
7934760c2415Smrg }
7935760c2415Smrg
7936760c2415Smrg /// ditto
7937760c2415Smrg Ternary opBinary(string s)(Ternary rhs) if (s == "&")
7938760c2415Smrg {
7939760c2415Smrg return make((26_144 >> (value + rhs.value)) & 6);
7940760c2415Smrg }
7941760c2415Smrg
7942760c2415Smrg /// ditto
7943760c2415Smrg Ternary opBinary(string s)(Ternary rhs) if (s == "^")
7944760c2415Smrg {
7945760c2415Smrg return make((26_504 >> (value + rhs.value)) & 6);
7946760c2415Smrg }
7947760c2415Smrg }
7948760c2415Smrg
7949760c2415Smrg ///
7950760c2415Smrg @safe @nogc nothrow pure
7951760c2415Smrg unittest
7952760c2415Smrg {
7953760c2415Smrg Ternary a;
7954760c2415Smrg assert(a == Ternary.unknown);
7955760c2415Smrg
7956760c2415Smrg assert(~Ternary.yes == Ternary.no);
7957760c2415Smrg assert(~Ternary.no == Ternary.yes);
7958760c2415Smrg assert(~Ternary.unknown == Ternary.unknown);
7959760c2415Smrg }
7960760c2415Smrg
7961760c2415Smrg @safe @nogc nothrow pure
7962760c2415Smrg unittest
7963760c2415Smrg {
7964760c2415Smrg alias f = Ternary.no, t = Ternary.yes, u = Ternary.unknown;
7965760c2415Smrg Ternary[27] truthTableAnd =
7966760c2415Smrg [
7967760c2415Smrg t, t, t,
7968760c2415Smrg t, u, u,
7969760c2415Smrg t, f, f,
7970760c2415Smrg u, t, u,
7971760c2415Smrg u, u, u,
7972760c2415Smrg u, f, f,
7973760c2415Smrg f, t, f,
7974760c2415Smrg f, u, f,
7975760c2415Smrg f, f, f,
7976760c2415Smrg ];
7977760c2415Smrg
7978760c2415Smrg Ternary[27] truthTableOr =
7979760c2415Smrg [
7980760c2415Smrg t, t, t,
7981760c2415Smrg t, u, t,
7982760c2415Smrg t, f, t,
7983760c2415Smrg u, t, t,
7984760c2415Smrg u, u, u,
7985760c2415Smrg u, f, u,
7986760c2415Smrg f, t, t,
7987760c2415Smrg f, u, u,
7988760c2415Smrg f, f, f,
7989760c2415Smrg ];
7990760c2415Smrg
7991760c2415Smrg Ternary[27] truthTableXor =
7992760c2415Smrg [
7993760c2415Smrg t, t, f,
7994760c2415Smrg t, u, u,
7995760c2415Smrg t, f, t,
7996760c2415Smrg u, t, u,
7997760c2415Smrg u, u, u,
7998760c2415Smrg u, f, u,
7999760c2415Smrg f, t, t,
8000760c2415Smrg f, u, u,
8001760c2415Smrg f, f, f,
8002760c2415Smrg ];
8003760c2415Smrg
8004760c2415Smrg for (auto i = 0; i != truthTableAnd.length; i += 3)
8005760c2415Smrg {
8006760c2415Smrg assert((truthTableAnd[i] & truthTableAnd[i + 1])
8007760c2415Smrg == truthTableAnd[i + 2]);
8008760c2415Smrg assert((truthTableOr[i] | truthTableOr[i + 1])
8009760c2415Smrg == truthTableOr[i + 2]);
8010760c2415Smrg assert((truthTableXor[i] ^ truthTableXor[i + 1])
8011760c2415Smrg == truthTableXor[i + 2]);
8012760c2415Smrg }
8013760c2415Smrg
8014760c2415Smrg Ternary a;
8015760c2415Smrg assert(a == Ternary.unknown);
8016760c2415Smrg static assert(!is(typeof({ if (a) {} })));
8017760c2415Smrg assert(!is(typeof({ auto b = Ternary(3); })));
8018760c2415Smrg a = true;
8019760c2415Smrg assert(a == Ternary.yes);
8020760c2415Smrg a = false;
8021760c2415Smrg assert(a == Ternary.no);
8022760c2415Smrg a = Ternary.unknown;
8023760c2415Smrg assert(a == Ternary.unknown);
8024760c2415Smrg Ternary b;
8025760c2415Smrg b = a;
8026760c2415Smrg assert(b == a);
8027760c2415Smrg assert(~Ternary.yes == Ternary.no);
8028760c2415Smrg assert(~Ternary.no == Ternary.yes);
8029760c2415Smrg assert(~Ternary.unknown == Ternary.unknown);
8030760c2415Smrg }
8031