1 /* 2 MIT License 3 Copyright (c) 2017 Boris Barboris 4 */ 5 6 module daii; 7 8 import std.experimental.allocator.mallocator: Mallocator; 9 10 static import daii.unique; 11 static import daii.refcounted; 12 static import daii.closure; 13 import daii.utils; 14 15 template AllocationContext(Allocator = Mallocator, bool Atomic = true) 16 if (isAllocator!Allocator) 17 { 18 template Unique(T) 19 { 20 alias Unique = daii.unique.Unique!(T, Allocator); 21 } 22 23 template RefCounted(T) 24 { 25 alias RefCounted = daii.refcounted.RefCounted!(T, Atomic, Allocator); 26 } 27 28 alias Delegate = daii.closure.AllocationContext!(Allocator, Atomic).Delegate; 29 30 alias autodlg = daii.closure.AllocationContext!(Allocator, Atomic).autodlg; 31 } 32 33 // main for unittests to run 34 void main(){} 35 36 37 38 /* Examples */ 39 unittest 40 { 41 // Create a shortcut to allocation context: 42 alias MallocCtx = AllocationContext!(Mallocator, true); 43 // MyCtx will contain primitives, that use static malloc and atomic operations 44 // where applicable. Currently, Atomic option is only used in reference counter 45 // decrement\increment of RefCounted struct. 46 47 // Create some aliases up to your taste 48 alias Unique = MallocCtx.Unique; 49 alias RefCounted = MallocCtx.RefCounted; 50 alias Delegate = MallocCtx.Delegate; 51 alias autodlg = MallocCtx.autodlg; 52 53 // Unique 54 { 55 static int ades = 0; 56 class A { ~this() { ades++; } } 57 static int bdes = 0; 58 class B: A 59 { 60 this(int x_init) { x = x_init; } 61 int x; 62 ~this(){ bdes++; } 63 } 64 { 65 Unique!A aq = Unique!A.make(); 66 { 67 Unique!A bq = Unique!B.make(5); // supports upcasting 68 // Unique is not a proxy for the resource, 69 // access underlying object by .v property. 70 B val = cast(B) bq.v; 71 assert(val.x == 5); 72 val.x = 4; 73 assert(val.x == 4); 74 } // bq destroyed 75 assert(bdes == 1); // instance of B was destroyed 76 assert(ades == 1); // A's destructor called as well obviously 77 Unique!A cq = aq.move; // use move to transfer ownership 78 assert(!aq.valid); // aq no longer holds value 79 void consume(Unique!A ptr) 80 { 81 assert(ptr.valid); 82 } // ptr destroyed 83 consume(cq.move); // use move to transfer ownership 84 assert(ades == 2); // consume destroyed the resource held in cq 85 assert(bdes == 1); 86 assert(!cq.valid); 87 } 88 } 89 90 // RefCounted 91 { 92 static int adec = 0; 93 class C { ~this() { adec++; } } 94 static int bdec = 0; 95 class D: C { ~this() { bdec++; }} 96 { 97 RefCounted!C ag = RefCounted!C.make(); 98 { 99 RefCounted!C aq = ag; // copy constructor is not disabled 100 assert(aq.valid && ag.valid); 101 { 102 RefCounted!C bq = RefCounted!D.make(); // upcast OK 103 } // bq destroyed 104 assert(adec == 1); 105 assert(bdec == 1); 106 } // aq out of scope, but it's resource is not destroyed 107 // ag still holds the valid pointer 108 assert(ag.valid); 109 assert(adec == 1); 110 assert(bdec == 1); 111 } // ag is the last owner, destructor called on A's instance 112 assert(adec == 2); 113 assert(bdec == 1); 114 } 115 116 // Delegate 117 { 118 // case1 119 { 120 int sum = 0; 121 int[] arr = [3, 5, 1, 9, 4]; 122 // Delegate!(T1, T2, T3) is a struct, that overloads opCall with signature 123 // T1 opCall(T2 p1, T3 p2).First template parameter is the return type. 124 // Use `void` if it's a procedure. 125 void map(int[] arr, Delegate!(void, int) dlg) 126 { 127 for (int i = 0; i < arr.length; i++) 128 dlg(arr[i]); 129 } 130 // autodlg function deduces delegate type from it's arguments. 131 // It's first argument is allocator instance. Since we use allocation 132 // context with static allocator (Mallocator.instance), we don't 133 // need to pass it here. 134 // It's second argument is function pointer. 135 // The rest arguments are captured variables. Here, `sum` is captured 136 // by pointer. 137 // `sum` is bound to `s` function parameter. `x` is taken from 138 // the opCall of resulting Delegate. 139 auto dlg = autodlg((int x, int* s) { *s += x; }, &sum); 140 map(arr, dlg); 141 assert(sum == 22); 142 } 143 // case2 144 { 145 static int counter = 0; 146 // This call to autodlg constructs eponymous closure, that holds 147 // one int field. `int` type is deduced from `0`, not from `ref int ctr`. 148 // Function gets the reference `ref int ctr` to said 149 // int field of the closure. 150 Delegate!(void) dlg = autodlg((ref int ctr) { ctr++; counter = ctr; }, 0); 151 dlg(); 152 dlg(); 153 assert(counter == 2); 154 // Internally dlg holds a reference-counted instance of abstract 155 // class Closure. Body of this eponymous derived class contains the 156 // int field. 157 void consumeDlg(Delegate!(void) d) { d(); } // the same closure 158 consumeDlg(dlg); 159 assert(counter == 3); 160 // OpEquals is overloaded to compare closure identity 161 auto dlg2 = dlg; 162 assert(dlg2 == dlg); 163 } 164 // case3 165 { 166 import std.algorithm.comparison: equal; 167 import std.container.array: Array; 168 169 static string global_str; 170 class EventGenerator 171 { 172 Array!(Delegate!(void, string)) handlers; 173 void raise(string s) 174 { 175 foreach (dlg; handlers) 176 dlg(s); 177 } 178 } 179 class EventReciever 180 { 181 string mystr; 182 this(string custom_str) { mystr = custom_str; } 183 void recieve(string evt) 184 { 185 global_str ~= mystr; 186 global_str ~= evt; 187 } 188 ~this() 189 { 190 global_str ~= "D"; 191 } 192 } 193 { 194 auto gen = RefCounted!EventGenerator.make; 195 { 196 auto rec1 = RefCounted!EventReciever.make("Fizz"); 197 auto rec2 = RefCounted!EventReciever.make("Buzz"); 198 gen.v.handlers ~= autodlg( 199 (string s, typeof(rec1) rc){rc.v.recieve(s);}, rec1); 200 gen.v.handlers ~= autodlg( 201 (string s, typeof(rec2) rc){rc.v.recieve(s);}, rec2); 202 } // EventRecievers are still referenced from get.v.handlers array 203 gen.v.raise("K"); 204 assert(equal(global_str, "FizzKBuzzK")); 205 } 206 // gen destroyed, array destructor destroys all closured. 207 // no more references to EventRecievers, their destructors run. 208 assert(equal(global_str, "FizzKBuzzKDD")); 209 } 210 } 211 212 // If you want to use instantiated allocator: 213 import std.experimental.allocator.showcase; 214 alias AllocType = StackFront!(4096, Mallocator); 215 AllocType allocator; 216 // Note the AllocType*. Pointer to structure AllocType is used as 217 // allocator, not the structure itself. 218 alias StackFrontCtx = AllocationContext!(AllocType*, true); 219 220 // Construct primitives like this: 221 auto unq = StackFrontCtx.Unique!(int).make(&allocator, 5); 222 assert(unq.v == 5); 223 auto rcp = StackFrontCtx.RefCounted!(int).make(&allocator, 5); 224 assert(rcp.v == 5); 225 auto ddlg = StackFrontCtx.autodlg(&allocator, (int* x) { *x = 4; }); 226 int k = 0; 227 ddlg(&k); 228 assert(k == 4); 229 }