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