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 }