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 }