1 /** 2 Base mechanisms used to determine information about iopipes. 3 Copyright: Copyright Steven Schveighoffer 2011-. 4 License: Boost License 1.0. (See accompanying file LICENSE_1_0.txt or copy 5 at http://www.boost.org/LICENSE_1_0.txt) 6 Authors: Steven Schveighoffer 7 */ 8 module iopipe.traits; 9 10 /** 11 * add window property to all arrays that allows any array to be the start of a pipe. 12 * Returns: t 13 */ 14 auto window(T)(T[] t) 15 { 16 return t; 17 } 18 19 /** 20 * add extend function to all arrays that allows any array to be the start of a pipe chain. 21 * Params: t = The array to attempt to extend. 22 * elements = ignored 23 * Returns: Always returns 0 because arrays cannot be extended. 24 */ 25 size_t extend(T)(T[] t, size_t elements) 26 { 27 return 0; 28 } 29 30 /** 31 * Add release function to all arrays. This will remove the given number of elements 32 * from the front of the array 33 * Params: t = The array to release elements from. 34 * elements = Number of elements to release 35 */ 36 void release(T)(ref T[] t, size_t elements) 37 { 38 assert(elements <= t.length); 39 t = t[elements .. $]; 40 } 41 42 @safe unittest 43 { 44 // ensure an array is a valid iopipe 45 static assert(isIopipe!(ubyte[])); 46 static assert(isIopipe!(string)); 47 static assert(isIopipe!(int[])); 48 49 // release is the only testworthy function 50 import std.range: iota; 51 import std.array: array; 52 53 auto arr = iota(100).array; 54 55 auto oldarr = arr; 56 arr.release(20); 57 assert(oldarr[20 .. $] == arr); 58 } 59 60 /** 61 * evaluates to true if the given type is a valid ioPipe 62 */ 63 template isIopipe(T) 64 { 65 enum isIopipe = is(typeof(() 66 { 67 import std.range.primitives; 68 import std.traits; 69 auto t = T.init; 70 auto window = t.window; 71 alias W = typeof(window); 72 static assert(isNarrowString!W || isRandomAccessRange!W); 73 auto x = t.extend(size_t(0)); 74 static assert(is(typeof(x) == size_t)); 75 t.release(size_t(0)); 76 })); 77 } 78 79 @safe unittest 80 { 81 import std.meta: AliasSeq; 82 import std.traits: isNarrowString; 83 static struct S1(T) 84 { 85 T[] window; 86 size_t extend(size_t elements) { return 0; } 87 void release(size_t elements) {} 88 } 89 90 // test struct with random access range instead of array 91 import std.range: chain; 92 static struct S2(T) 93 { 94 T[] arr1; 95 T[] arr2; 96 auto window() { return chain(arr1, arr2); } 97 size_t extend(size_t elements) { return 0; } 98 void release(size_t elements) {} 99 } 100 101 foreach(type; AliasSeq!(char, wchar, dchar, ubyte, byte, ushort, short, uint, int)) 102 { 103 static assert(isIopipe!(S1!type), "S1!" ~ type.stringof); 104 // Phobos treats narrow strings as non-random access range of dchar, so 105 // compositions will not work with iopipe. 106 static if(!isNarrowString!(type[])) 107 static assert(isIopipe!(S2!type), "S2!" ~ type.stringof); 108 } 109 } 110 111 // I don't know how to do this a better way... 112 template PropertyType(alias x) 113 { 114 import std.traits: ReturnType; 115 static if(is(typeof(x) == function)) 116 alias PropertyType = ReturnType!x; 117 else 118 alias PropertyType = typeof(x); 119 } 120 121 /** 122 * Determine the type of the window of the given pipe type. This works when the 123 * window is a method or a field. 124 */ 125 template WindowType(T) 126 { 127 alias WindowType = PropertyType!(T.init.window); 128 } 129 130 @safe unittest 131 { 132 static struct S1 { ubyte[] window; } 133 static assert(is(WindowType!S1 == ubyte[])); 134 135 static struct S2 { ubyte[] window() { return null; } } 136 static assert(is(WindowType!S2 == ubyte[])); 137 } 138 139 /** 140 * Evaluates to true if the given io pipe has a valve 141 */ 142 template hasValve(T) 143 { 144 import std.traits : hasMember; 145 static if(hasMember!(T, "valve")) 146 enum hasValve = isIopipe!(PropertyType!(T.init.valve)); 147 else 148 enum hasValve = false; 149 } 150 151 /** 152 * Boilerplate for implementing a valve. If you don't define a custom valve, 153 * you should always mixin this template in all your iopipe templates. 154 * 155 * Params: pipechain = symbol that contains the upstream pipe chain. 156 */ 157 mixin template implementValve(alias pipechain) 158 { 159 static if(hasValve!(PropertyType!(pipechain))) 160 ref valve() { return pipechain.valve; } 161 } 162 163 @safe unittest 164 { 165 static struct S1 166 { 167 int[] valve; 168 size_t extend(size_t elements) { return elements; } 169 int[] window; 170 void release(size_t elements) {} 171 } 172 173 static assert(hasValve!S1); 174 175 static struct S2(T) 176 { 177 T upstream; 178 size_t extend(size_t elements) { return elements; } 179 int[] window; 180 void release(size_t elements) {} 181 182 mixin implementValve!(upstream); 183 } 184 185 static assert(hasValve!(S2!S1)); 186 static assert(!hasValve!(S2!(int[]))); 187 } 188 189 /** 190 * Determine the number of valves in the given pipeline 191 */ 192 template valveCount(T) 193 { 194 static if(hasValve!(T)) 195 { 196 enum valveCount = 1 + .valveCount!(PropertyType!(T.init.valve)); 197 } 198 else 199 { 200 enum valveCount = 0; 201 } 202 } 203 204 @safe unittest 205 { 206 static struct ValveStruct(T, bool shouldAddValve) 207 { 208 static if(shouldAddValve) 209 { 210 T valve; 211 } 212 else 213 { 214 T upstream; 215 mixin implementValve!(upstream); 216 } 217 int[] window; 218 size_t extend(size_t elements) { return elements; } 219 void release(size_t elements) {} 220 221 } 222 223 static void foo(bool shouldAddValve, int curValves, int depth, T)(T t) 224 { 225 auto p = ValveStruct!(T, shouldAddValve)(); 226 enum myValves = curValves + (shouldAddValve? 1 : 0); 227 static assert(valveCount!(typeof(p)) == myValves); 228 static if(depth > 0) 229 { 230 foo!(true, myValves, depth - 1)(p); 231 foo!(false, myValves, depth - 1)(p); 232 } 233 } 234 235 foo!(true, 0, 4)((int[]).init); 236 foo!(false, 0, 4)((int[]).init); 237 }