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 }