1 /**
2 * ae.utils.meta.chain
3 *
4 * License:
5 * This Source Code Form is subject to the terms of
6 * the Mozilla Public License, v. 2.0. If a copy of
7 * the MPL was not distributed with this file, You
8 * can obtain one at http://mozilla.org/MPL/2.0/.
9 *
10 * Authors:
11 * Vladimir Panteleev <vladimir@thecybershadow.net>
12 */
13
14 /// Chains are a concept a bit like ranges,
15 /// but which operate on heterogenous types
16 /// (e.g. a tuple of values).
17
18 /// Composition is done by a chain of functors.
19 /// To allow state, each functor can be represented
20 /// as a struct.
21
22 /// Functors return a bool (true if iteration should
23 /// stop, false if it should continue).
24
25 module ae.utils.meta.chain;
26
27 import ae.utils.meta.caps;
28
29 ///
30 static if (haveAliasStructBinding)
31 unittest
32 {
33 int a = 2;
34 int x;
35 chainIterator(chainFilter!(n => n > a)((int n) { x = n; return true; } ))(1, 2, 3);
36 assert(x == 3);
37 }
38
39 static if (haveAliasStructBinding)
40 unittest
41 {
42 static struct X
43 {
44 bool fun(int x)
45 {
46 return chainIterator(chainFunctor!(n => n == x))(this.tupleof);
47 }
48
49 long a;
50 int b;
51 ubyte c;
52 }
53
54 X x;
55 assert(!x.fun(5));
56 x.c = 5;
57 assert(x.fun(5));
58 }
59
60 /// Starts the chain by iterating over a tuple.
61 struct ChainIterator(Next)
62 {
63 Next next;
64
65 this(ref Next next)
66 {
67 this.next = next;
68 }
69
70 bool opCall(Args...)(auto ref Args args)
71 {
72 foreach (ref arg; args)
73 if (next(arg))
74 return true;
75 return false;
76 }
77 }
78 auto chainIterator(Next)(Next next)
79 {
80 return ChainIterator!Next(next);
81 }
82
83 unittest
84 {
85 struct S
86 {
87 int a = 1;
88 long b = 2;
89 ubyte c = 3;
90 }
91 S s;
92
93 int[] results;
94 chainIterator((long n) { results ~= cast(int)n; return false; })(s.tupleof);
95 assert(results == [1, 2, 3]);
96 }
97
98 /// Wraps a function template into a concrete value type functor.
99 struct ChainFunctor(alias fun)
100 {
101 auto opCall(Arg)(auto ref Arg arg)
102 {
103 return fun(arg);
104 }
105 }
106 auto chainFunctor(alias fun)() /// ditto
107 {
108 ChainFunctor!fun s;
109 return s;
110 }
111
112 ///
113 static if (haveAliasStructBinding)
114 unittest
115 {
116 int[] results;
117 auto fn = chainFunctor!(n => results ~= cast(int)n);
118 fn(1);
119 fn(long(2));
120 fn(ubyte(3));
121 assert(results == [1, 2, 3]);
122 }
123
124 /// Calls next only if pred(value) is true.
125 struct ChainFilter(alias pred, Next)
126 {
127 Next next;
128
129 this(Next next) { this.next = next; }
130
131 bool opCall(T)(auto ref T v)
132 {
133 if (pred(v))
134 return next(v);
135 return false;
136 }
137 }
138 template chainFilter(alias pred) /// ditto
139 {
140 auto chainFilter(Next)(Next next)
141 {
142 return ChainFilter!(pred, Next)(next);
143 }
144 }
145
146 struct ChainControl(bool result, Next)
147 {
148 Next next;
149
150 this(Next next) { this.next = next; }
151
152 bool opCall(T)(auto ref T v)
153 {
154 cast(void)next(v);
155 return result;
156 }
157 }
158 template chainControl(bool result) /// ditto
159 {
160 auto chainControl(Next)(Next next)
161 {
162 return ChainControl!(result, Next)(next);
163 }
164 }
165 alias chainAll = chainControl!false; // Always continue iteration
166 alias chainFirst = chainControl!true; // Stop iteration after this element
167
168 ///
169 static if (haveAliasStructBinding)
170 unittest
171 {
172 int a = 2;
173 int b = 3;
174 int[] results;
175 foreach (i; 0..10)
176 chainFilter!(n => n % a == 0)(chainFilter!(n => n % b == 0)((int n) { results ~= n; return false; }))(i);
177 assert(results == [0, 6]);
178 }
179
180 /// Calls next with pred(value).
181 struct ChainMap(alias pred, Next)
182 {
183 Next next;
184
185 this(Next next) { this.next = next; }
186
187 bool opCall(T)(auto ref T v)
188 {
189 return next(pred(v));
190 }
191 }
192 template chainMap(alias pred) /// ditto
193 {
194 auto chainMap(Next)(Next next)
195 {
196 return ChainMap!(pred, Next)(next);
197 }
198 }
199
200 ///
201 unittest
202 {
203 int result;
204 chainMap!(n => n+1)(chainMap!(n => n * 2)((int n) { result = n; return false; }))(2);
205 assert(result == 6);
206 }