1 // 2 // May destructuring variables for itarable types and tuples 3 // 4 5 module vest.utils.tie; 6 7 import std.range : empty, popFront, front; 8 import std.traits : isIterable; 9 import std.typecons : Tuple, tuple, isTuple; 10 import std.functional : forward; 11 import std.meta : staticMap; 12 13 auto tie(Args...)(ref Args args) 14 { 15 static assert(Args.length, "don't call with emprty args"); 16 return TieInstance!(Args)(args); 17 } 18 19 private alias ptr_t(T) = T*; 20 21 /// Helper structure to collect pointers to arguments 22 private struct PointersSeq(Args...) 23 { 24 alias arg_ptrs_t(T...) = Tuple!(staticMap!(ptr_t, T)); 25 arg_ptrs_t!(Args) items; 26 27 this(ref Args args) 28 { 29 foreach(index, Arg; Args) { 30 items[index] = &args[index]; 31 } 32 } 33 void set(size_t index, T)(auto ref T rhs) 34 { 35 static assert(index < Args.length); 36 *items[index] = cast(Args[index]) rhs; 37 } 38 } 39 40 /// Helper structure. 41 /// Due to the overload of assignment operators allows to use expression tie(x, y, ...) = ... 42 private struct TieInstance(Args...) 43 { 44 PointersSeq!(Args) items; 45 46 // In the constructor retrive arguments pointers 47 this(ref Args args) 48 { 49 items = PointersSeq!(Args)(args); 50 } 51 52 // overloading assignment operator (empty Tuple) 53 void opAssign(Tuple!() rhs) {} 54 55 // overloading assignment operator 56 void opAssign(Types...)(auto ref Tuple!(Types) rhs) 57 { 58 foreach(index, T; Types) { 59 static if(index < Args.length) { 60 items.set!index( rhs[index] ); 61 } 62 } 63 } 64 65 // Helper method to iterate at compile time 66 pragma(inline, true) 67 private void applyTravers(size_t index, T)(auto ref T rhs) 68 if(isIterable!T && !isTuple!T) 69 { 70 if(rhs.empty) return; 71 72 items.set!index(rhs.front); 73 74 rhs.popFront(); 75 static if(index < Args.length-1) { 76 applyTravers!(index+1, T)(forward!rhs); 77 } 78 static assert(index < Args.length); 79 } 80 81 // overloading assignment operator for iterable types 82 void opAssign(T)(auto ref T rhs) 83 if(isIterable!T && !isTuple!T) 84 { 85 applyTravers!(0, T)( forward!rhs ); 86 } 87 } 88 89 90 91 // to run tests: dmd -unittest -main vest/utils/tie.d && ./vest/utils/tie 92 // or: cd source 93 // rdmd -unittest -main vest/utils/tie 94 nothrow unittest { 95 96 // Traversable 97 string a, b, c, d, e; 98 tie(a,b,c,d,e) = ["foo1","foo2","foo3","foo4","foo5"]; 99 assert([a,b,c,d,e] == ["foo1","foo2","foo3","foo4","foo5"]); 100 101 tie(a,b,c) = ["bar1","bar2"]; 102 assert([a,b,c] == ["bar1", "bar2", "foo3"]); 103 104 tie(a,b,c); 105 assert([a,b,c] == ["bar1", "bar2", "foo3"]); 106 107 tie(c,c,c,d,e) = ["hru1","hru2"]; 108 assert([a,b,c,d,e] == ["bar1","bar2","hru2","foo4","foo5"]); 109 110 size_t i, j, k; 111 112 tie(i,j,k) = [1,2]; 113 assert([i,j,k] == [1, 2, size_t.init]); 114 115 tie(i,j,k) = [50,60,70,80,90]; 116 assert([i,j,k] == [50,60,70]); 117 118 tie(i,j,k) = [-1,3.14]; 119 assert([i,j,k] == [size_t.max, 3, 70]); 120 121 double pi; 122 int l; 123 124 tie(i,pi,l) = [3.14, 3.14, 3.14]; 125 assert(tuple(i,pi,l) == tuple(size_t(3), double(3.14), 3)); 126 127 tie(i,pi,l) = [size_t.max, size_t.max, size_t.max]; 128 assert( tuple(i,pi,l) == tuple(size_t.max, cast(double) size_t.max, cast(int) size_t.max ) ); 129 130 // Tuples 131 int x; 132 string y; 133 char z; 134 size_t u,v,w; 135 136 tie(x,y,z) = tuple(26, " hello ", 'a', 777, 3.14); 137 assert(x == 26); 138 assert(y == " hello "); 139 assert(z == 'a'); 140 141 tie(x,y,z) = tuple(); 142 assert(x == 26); 143 assert(y == " hello "); 144 assert(z == 'a'); 145 146 tie(x, y, z) = tuple(15, " world "); 147 assert(x == 15); 148 assert(y == " world "); 149 assert(z == 'a'); 150 151 152 tie(x, y, z); 153 assert(x == 15); 154 assert(y == " world "); 155 assert(z == 'a'); 156 157 tie(x, y, z) = tuple(); 158 assert(x == 15); 159 assert(y == " world "); 160 assert(z == 'a'); 161 162 tie(x) = tuple(50); 163 assert(x == 50); 164 165 //tie() = tuple(15, " hello "); <-- don't call with emprty args 166 167 tie(x,y,z,u,v,w) = tuple(-5, " hi ", 'b', 48, 49, 50, 777, 3.14); 168 assert(tuple(x,y,z, [u,v,w]) == tuple(-5, " hi ", 'b', [48, 49, 50])); 169 170 tie(u,v,w,y) = tuple(15,16,17); 171 assert([u,v,w] == [15,16,17]); 172 173 174 tie(v,v,v,y,x,z) = tuple(25,26,27); 175 assert([u,v,w] == [15,27,17]); 176 177 tie(v,u) = tuple(u,v); 178 assert([u,v] == [27,15]); 179 } 180 181 unittest { 182 import std.range : iota; 183 size_t i, j, k, l, m; 184 tie(i,j,k) = iota(50, 91, 10); 185 assert([i,j,k] == [50,60,70]); 186 187 struct TestItrbl 188 { 189 int a = 0; 190 @property bool empty() {return a >= 10;} 191 @property int front() {return a;} 192 void popFront() {++a;} 193 } 194 195 tie(i,j,k,l,m) = TestItrbl(); 196 assert([i,j,k,l,m] == [0,1,2,3,4]); 197 198 }