1 /** Templates for declaring and examining static interfaces of pipeline stages. 2 * 3 * Authors: $(LINK2 https://github.com/epi, Adrian Matoga) 4 * Copyright: © 2016 Adrian Matoga 5 * License: $(LINK2 http://boost.org/LICENSE_1_0.txt, Boost License 1.0). 6 */ 7 module flod.traits; 8 9 import flod.meta : str, Id; 10 11 struct PullSource(E) { 12 size_t pull(E[] buf) { return buf.length; } 13 enum methodStr = "pull"; 14 } 15 16 struct PeekSource(E) { 17 const(E)[] peek(size_t n) { return new E[n]; } 18 void consume(size_t n) {} 19 enum methodStr = "peek"; 20 } 21 22 struct PushSource(E) { 23 enum methodStr = "push"; 24 } 25 26 struct AllocSource(E) { 27 enum methodStr = "alloc"; 28 } 29 30 struct PullSink(E) { 31 enum methodStr = "pull"; 32 } 33 34 struct PeekSink(E) { 35 enum methodStr = "peek"; 36 } 37 38 struct PushSink(E) { 39 size_t push(const(E)[] buf) { return buf.length; } 40 enum methodStr = "push"; 41 } 42 43 struct AllocSink(E) { 44 bool alloc(ref E[] buf, size_t n) { buf = new E[n]; return true; } 45 void consume(size_t n) {} 46 enum methodStr = "alloc"; 47 } 48 49 enum pullSource(E) = PullSource!E(); 50 enum peekSource(E) = PeekSource!E(); 51 enum pushSource(E) = PushSource!E(); 52 enum allocSource(E) = AllocSource!E(); 53 54 enum pullSink(E) = PullSink!E(); 55 enum peekSink(E) = PeekSink!E(); 56 enum pushSink(E) = PushSink!E(); 57 enum allocSink(E) = AllocSink!E(); 58 59 private struct None {} 60 61 private template isSame(alias S) { 62 template isSame(alias Z) { 63 enum isSame = is(Id!S == Id!Z); 64 } 65 } 66 67 private template areSame(W...) { 68 enum areSame = is(Id!(W[0 .. $ / 2]) == Id!(W[$ / 2 .. $])); 69 } 70 71 package template Traits(alias Src, alias Snk, SrcE, SnkE, UDAs...) { 72 static if (UDAs.length == 0) { 73 alias Source = Src; 74 alias Sink = Snk; 75 alias SourceElementType = SrcE; 76 alias SinkElementType = SnkE; 77 static if (!is(Src == None)) 78 enum sourceMethodStr = Src!SrcE.methodStr; 79 else 80 enum sourceMethodStr = ""; 81 static if (!is(Snk == None)) 82 enum sinkMethodStr = Snk!SnkE.methodStr; 83 else 84 enum sinkMethodStr = ""; 85 enum str = .str!Sink ~ "!" ~ .str!SinkElementType ~ "-" ~ .str!Source ~ "!" ~ .str!SourceElementType; 86 } else { 87 import std.meta : anySatisfy; 88 89 static if (is(typeof(UDAs[0]))) { 90 alias T = typeof(UDAs[0]); 91 static if (is(T == S!E, alias S, E)) { 92 static if (anySatisfy!(isSame!S, PullSource, PeekSource, PushSource, AllocSource)) { 93 static assert(is(Id!Src == Id!None), "Source interface declared more than once"); 94 alias Traits = .Traits!(S, Snk, E, SnkE, UDAs[1 .. $]); 95 } else static if (anySatisfy!(isSame!S, PullSink, PeekSink, PushSink, AllocSink)) { 96 static assert(is(Id!Snk == Id!None), "Sink interface declared more than once"); 97 alias Traits = .Traits!(Src, S, SrcE, E, UDAs[1 .. $]); 98 } else { 99 alias Traits = .Traits!(Src, Snk, SrcE, SnkE, UDAs[1 .. $]); 100 } 101 } else { 102 alias Traits = .Traits!(Src, Snk, SrcE, SnkE, UDAs[1 .. $]); 103 } 104 } else { 105 alias Traits = .Traits!(Src, Snk, SrcE, SnkE, UDAs[1 .. $]); 106 } 107 } 108 } 109 110 template Traits(alias S) { 111 alias Traits = Traits!(None, None, void, void, __traits(getAttributes, S)); 112 } 113 114 unittest { 115 @peekSource!int @(100) @Id!"zombie"() @allocSink!(Id!1) 116 struct Foo {} 117 alias Tr = Traits!Foo; 118 static assert(areSame!(Tr.Source, PeekSource)); 119 static assert(areSame!(Tr.SourceElementType, int)); 120 static assert(areSame!(Tr.Sink, AllocSink)); 121 static assert(areSame!(Tr.SinkElementType, Id!1)); 122 } 123 124 unittest { 125 @pullSource!int @pushSource!ubyte 126 struct Bar {} 127 static assert(!__traits(compiles, Traits!Bar)); // source interface specified twice 128 } 129 130 unittest { 131 @pullSink!double @pushSink!string @peekSink!void 132 struct Baz {} 133 static assert(!__traits(compiles, Traits!Baz)); // sink interface specified 3x 134 } 135 136 enum isPullSource(alias S) = areSame!(Traits!S.Source, PullSource); 137 enum isPeekSource(alias S) = areSame!(Traits!S.Source, PeekSource); 138 enum isPushSource(alias S) = areSame!(Traits!S.Source, PushSource); 139 enum isAllocSource(alias S) = areSame!(Traits!S.Source, AllocSource); 140 141 enum isPullSink(alias S) = areSame!(Traits!S.Sink, PullSink); 142 enum isPeekSink(alias S) = areSame!(Traits!S.Sink, PeekSink); 143 enum isPushSink(alias S) = areSame!(Traits!S.Sink, PushSink); 144 enum isAllocSink(alias S) = areSame!(Traits!S.Sink, AllocSink); 145 146 unittest { 147 @pullSource!int @pullSink!bool 148 struct Foo {} 149 static assert( isPullSource!Foo); 150 static assert(!isPeekSource!Foo); 151 static assert(!isPushSource!Foo); 152 static assert(!isAllocSource!Foo); 153 static assert( isPullSink!Foo); 154 static assert(!isPeekSink!Foo); 155 static assert(!isPushSink!Foo); 156 static assert(!isAllocSink!Foo); 157 } 158 159 enum isPassiveSource(alias S) = isPeekSource!S || isPullSource!S; 160 enum isActiveSource(alias S) = isPushSource!S || isAllocSource!S; 161 enum isSource(alias S) = isPassiveSource!S || isActiveSource!S; 162 163 enum isPassiveSink(alias S) = isPushSink!S || isAllocSink!S; 164 enum isActiveSink(alias S) = isPeekSink!S || isPullSink!S; 165 enum isSink(alias S) = isPassiveSink!S || isActiveSink!S; 166 167 168 /** Returns `true` if `S[0]` is a source and `S[1]` is a sink and they both use the same 169 * method of passing data. 170 */ 171 enum areCompatible(alias Source, alias Sink) = 172 (isPeekSource!Source && isPeekSink!Sink) 173 || (isPullSource!Source && isPullSink!Sink) 174 || (isAllocSource!Source && isAllocSink!Sink) 175 || (isPushSource!Source && isPushSink!Sink); 176 177 template sourceMethod(alias S) { 178 static if (isPeekSource!S) { 179 alias sourceMethod = peekSource!(Traits!S.SourceElementType); 180 } else static if (isPullSource!S) { 181 alias sourceMethod = pullSource!(Traits!S.SourceElementType); 182 } else static if (isAllocSource!S) { 183 alias sourceMethod = allocSource!(Traits!S.SourceElementType); 184 } else static if (isPushSource!S) { 185 alias sourceMethod = pushSource!(Traits!S.SourceElementType); 186 } else { 187 bool sourceMethod() { return false; } 188 } 189 } 190 191 template sinkMethod(alias S) { 192 static if (isPeekSink!S) { 193 alias sinkMethod = peekSink!(Traits!S.SinkElementType); 194 } else static if (isPullSink!S) { 195 alias sinkMethod = pullSink!(Traits!S.SinkElementType); 196 } else static if (isAllocSink!S) { 197 alias sinkMethod = allocSink!(Traits!S.SinkElementType); 198 } else static if (isPushSink!S) { 199 alias sinkMethod = pushSink!(Traits!S.SinkElementType); 200 } else { 201 bool sinkMethod() { return false; } 202 } 203 } 204 205 unittest { 206 @peekSource!double static struct Foo {} 207 static assert(sourceMethod!Foo == peekSource!double); 208 } 209 210 mixin template TestScheduler() { 211 static struct __Scheduler { 212 void stop(); 213 } 214 __Scheduler _flod_scheduler; 215 216 int yield() { return 1; } 217 } 218 219 bool check(alias T)() 220 { 221 if (__ctfe) { 222 alias SoE = Traits!T.SourceElementType; 223 alias SiE = Traits!T.SinkElementType; 224 static if (isPushSink!T && isPullSource!T) { 225 import flod.pipeline : AllWrapper; 226 AllWrapper!(T!TestScheduler) t; 227 SiE[15] pushbuf; 228 SoE[15] pullbuf; 229 static if (!__traits(compiles, t.push(pushbuf[]))) 230 t.push(pushbuf[]); 231 static if (!__traits(compiles, t.pull(pullbuf[]))) 232 t.push(pullbuf[]); 233 } 234 else { 235 pragma(msg, "@check not implemented for ", str!(Traits!T), " (", str!T, ")"); 236 } 237 } 238 return true; 239 }