1 /**
2  * Copyright: Copyright (c) 2010-2011 Jacob Carlborg.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 29, 2010
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module orange.util.Use;
8 
9 import std.typetuple;
10 import std.typecons;
11 import std.traits;
12 
13 alias ReturnType ReturnTypeOf;
14 
15 /**
16  * This struct can be used to implement, what looks similar to, new statements. This
17  * struct overloads the "in" operator which a delegate can be passed to. The delegate
18  * passed to the "in" operator will then be called at an appropriate time decided by the
19  * implementation of the function returning the Use struct.
20  *
21  * Examples:
22  * ---
23  * Use!(void delegate (), bool) unless (bool condition)
24  * {
25  *     Use!(void delegate (), bool) use;
26  *     use.args[1] = condition;
27  *
28  *     use.args[0] = (void delegate () dg, bool condition) {
29  *         if (!condition)
30  *             dg();
31  *     };
32  *
33  *     return use;
34  * }
35  *
36  * int a = 3;
37  * int b = 4;
38  *
39  * unless(a == b) in {
40  *     println("a != b");
41  * };
42  * ---
43  */
44 struct Use (ARGS...)
45 {
46     static assert (ARGS.length > 0);
47 
48     private
49     {
50         alias ReturnTypeOf!(ARGS[0]) ReturnType;
51 
52         static if (ARGS.length >= 2)
53             alias Tuple!(ReturnType delegate (ARGS), ARGS[1 .. $]) NEW_ARGS;
54 
55         else
56             alias Tuple!(ReturnType delegate (ARGS)) NEW_ARGS;
57     }
58 
59     /**
60      * The first argument will be the delegate that performs some arbitrary operation. The
61      * rest of the arguments will be pass as arguments to the delegate in "args[0]".
62      */
63     NEW_ARGS args;
64 
65     /**
66      * Overloads the "in" operator. The given delegate is supplied by the user and will be
67      * called at a time the implementaion has decided.
68      *
69      * Params:
70      *     dg = the user supplied delegate that will be called
71      *
72      * Returns: what ever the delegate stored in "args[0]" returns
73      */
74     ReturnType opBinary(string op: "in")(ARGS[0] dg)
75     {
76         assert(args[0]);
77 
78         // Issue: https://d.puremagic.com/issues/show_bug.cgi?id=11614
79         static if (args.length == 1)
80             return args[0](dg);
81 
82         else
83             return args[0](dg, args.expand[1 .. $]);
84     }
85 }
86 
87 /**
88  * This is a helper struct used by the "restore" function. It overloads the "in" operator
89  * to allow to taking a delegate.
90  */
91 struct RestoreStruct (U, T)
92 {
93     /// The delegate that performs the operation.
94     U delegate(U delegate (), ref T) dg;
95 
96     /// A pointer to the value to pass to the delegate.
97     T* value;
98 
99     /**
100      * Overloads the "in" operator. It will simply call the delegate stored in the struct
101      * passing in the given delegate and the value stored in the struct.
102      *
103      * Params:
104      *     deleg = the delegate to pass the delegate stored in the struct
105      *
106      * Returns: whatever the delegate stored in the struct returns
107      *
108      * See_Also: restore
109      */
110     U opBinary(string op: "in")(U delegate () deleg)
111     {
112         return dg(deleg, *value);
113     }
114 }
115 
116 /**
117  * Restores the given variable to the value it was when it was passed to the function
118  * after the delegate has finished.
119  *
120  * Examples:
121  * ---
122  * int a = 3;
123  *
124  * restore(a) in {
125  *     a = 4;
126  * }
127  *
128  * assert(a == 3);
129  * ---
130  *
131  * Params:
132  *     val = variable that will be restored
133  *
134  * Returns: a RestoreStruct
135  *
136  * See_Also: RestoreStruct
137  */
138 RestoreStruct!(U, T) restore (U = void, T) (ref T val)
139 {
140     RestoreStruct!(U, T) restoreStruct;
141 
142     restoreStruct.dg = (U delegate () dg, ref T value){
143         T t = value;
144 
145         static if (is(U == void))
146         {
147             dg();
148             value = t;
149         }
150 
151         else
152         {
153             auto result = dg();
154             value = t;
155 
156             return result;
157         }
158     };
159 
160     restoreStruct.value = &val;
161 
162     return restoreStruct;
163 }