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