1 module typecons_ex;
2 
3 // TODO Add to Phobos and refer to http://forum.dlang.org/thread/lzyqywovlmdseqgqfvun@forum.dlang.org#post-ibvkvjwexdafpgtsamut:40forum.dlang.org
4 // TODO Better with?:
5 /* inout(Nullable!T) nullable(T)(inout T a) */
6 /* { */
7 /*     return typeof(return)(a); */
8 /* } */
9 /* inout(Nullable!(T, nullValue)) nullable(alias nullValue, T)(inout T value) */
10 /* if (is (typeof(nullValue) == T)) */
11 /* { */
12 /*     return typeof(return)(value); */
13 /* } */
14 
15 public import ties;
16 
17 import std.typecons: Nullable, NullableRef;
18 
19 /** Instantiator for $(D Nullable).
20  */
21 auto nullable(T)(T a)
22 {
23     return Nullable!T(a);
24 }
25 unittest
26 {
27     auto x = 42.5.nullable;
28     assert(is(typeof(x) == Nullable!double));
29 }
30 
31 /** Instantiator for $(D Nullable).
32 */
33 auto nullable(alias nullValue, T)(T value)
34     if (is (typeof(nullValue) == T))
35 {
36     return Nullable!(T, nullValue)(value);
37 }
38 unittest
39 {
40     auto x = 3.nullable!(int.max);
41     assert(is (typeof(x) == Nullable!(int, int.max)));
42 }
43 
44 /** Instantiator for $(D NullableRef).
45  */
46 auto nullableRef(T)(T* a) @safe pure nothrow
47 {
48     return NullableRef!T(a);
49 }
50 unittest
51 {
52     auto x = 42.5;
53     auto xr = nullableRef(&x);
54     assert(!xr.isNull);
55     xr.nullify;
56     assert(xr.isNull);
57 }
58 
59 /** See also: http://forum.dlang.org/thread/jwdbjlobbilowlnpdzzo@forum.dlang.org
60  */
61 template New(T) if (is(T == class))
62 {
63     T New(Args...) (Args args) {
64         return new T(args);
65     }
66 }
67 
68 import std.traits: isArray, isUnsigned, isInstanceOf, isSomeString;
69 import std.range.primitives: hasSlicing;
70 
71 /** Check if $(D T) is castable to $(D U).
72  */
73 enum isCastableTo(T, U) = __traits(compiles, { T i = 0; cast(U)i; });
74 
75 enum isIndex(I) = (is(I == enum) ||
76                    isUnsigned!I || // TODO should we allow isUnsigned here?
77                    isCastableTo!(I, size_t));
78 
79 /** Check if $(D R) is indexable by $(D I). */
80 enum isIndexableBy(R, I) = (isArray!R && // TODO generalize to RandomAccessContainers. Ask on forum for hasIndexing!R.
81                             isIndex!I);
82 
83 unittest
84 {
85     static assert(isIndexableBy!(int[3], ubyte));
86 }
87 
88 /**
89    Check if $(D R) is indexable by $(D I).
90  */
91 enum isIndexableBy(R, alias I) = (isArray!R && // TODO generalize to RandomAccessContainers. Ask on forum for hasIndexing!R.
92                                   (isSomeString!(typeof(I))));
93 
94 unittest
95 {
96     static assert(isIndexableBy!(int[], "I"));
97 }
98 
99 mixin template genOps(T)
100 {
101     auto ref opIndex(T i) inout
102     {
103         assert(cast(size_t)i < _r.length, "Range violation with index " ~ T.stringof);
104         return _r[cast(size_t)i];
105     }
106     auto ref opIndexAssign(V)(V value, T i)
107     {
108         assert(cast(size_t)i < _r.length, "Range violation with index " ~ T.stringof);
109         return _r[cast(size_t)i] = value;
110     }
111 
112     static if (hasSlicing!R)
113     {
114         auto ref opSlice(T i, T j) inout             { return _r[cast(size_t)i ..
115                                                                  cast(size_t)j]; }
116         auto ref opSliceAssign(V)(V value, T i, T j) { return _r[cast(size_t)i ..
117                                                                  cast(size_t)j] = value; }
118     }
119 }
120 
121 /** Wrapper for $(D R) with Type-Safe $(D I)-Indexing.
122     See also: http://forum.dlang.org/thread/gayfjaslyairnzrygbvh@forum.dlang.org#post-gayfjaslyairnzrygbvh:40forum.dlang.org
123 
124     TODO Merge with https://github.com/rcorre/enumap
125 
126     TODO Use std.range.indexed when I is an enum with non-contigious
127     enumerators. Perhaps use among aswell.
128 
129     TODO Rename to something more concise such as [Bb]y.
130 
131     TODO Allow $(D I) to be a string and if so derive $(D Index) to be that string.
132 
133     TODO Support R being a static array:
134          - If I is an enum its number of elements should match R.length
135    */
136 struct IndexedBy(R, I)
137     if (isIndexableBy!(R, I))
138 {
139     alias Index = I;        /// indexing type
140     mixin genOps!I;
141     R _r;
142     alias _r this; // TODO Use opDispatch instead; to override only opSlice and opIndex
143 }
144 
145 /** Instantiator for $(D IndexedBy).
146  */
147 auto indexedBy(I, R)(R range)
148     if (isIndexableBy!(R, I))
149 {
150     return IndexedBy!(R, I)(range);
151 }
152 
153 struct IndexedBy(R, string I_ = "Index")
154     if (isArray!R &&
155         I_ != "I_") // prevent name lookup failure
156 {
157     mixin(q{ struct } ~ I_ ~
158           q{ {
159                   alias T = size_t;
160                   this(T ix) { this._ix = ix; }
161                   T opCast(U : T)() const { return _ix; }
162                   private T _ix = 0;
163               }
164           });
165     mixin genOps!(mixin(I_));
166     R _r;
167     alias _r this; // TODO Use opDispatch instead; to override only opSlice and opIndex
168 }
169 
170 /** Instantiator for $(D IndexedBy).
171  */
172 auto indexedBy(string I, R)(R range)
173     if (isArray!R &&
174         I != "I_") // prevent name lookup failure
175 {
176     return IndexedBy!(R, I)(range);
177 }
178 
179 /** Instantiator for $(D IndexedBy).
180  */
181 auto indexed(R)(R range)
182     if (isArray!R)
183 {
184     return IndexedBy!(R)(range);
185 }
186 
187 @safe pure nothrow unittest
188 {
189     int[3] x = [1, 2, 3];
190 
191     // sample index
192     struct Index(T = size_t)
193         if (isUnsigned!T)
194     {
195         this(T i) { this._i = i; }
196         T opCast(U : T)() const { return _i; }
197         private T _i = 0;
198     }
199     alias J = Index!size_t;
200 
201     enum E { e0, e1, e2 }
202 
203     with (E)
204     {
205         auto xb = x.indexedBy!ubyte;
206         auto xi = x.indexedBy!uint;
207         auto xj = x.indexedBy!J;
208         auto xe = x.indexedBy!E;
209         auto xf = x.indexed;
210 
211         auto xs = x.indexedBy!"I";
212         alias XS = typeof(xs);
213         XS xs_;
214 
215         // indexing with correct type
216         xb[  0 ] = 11; assert(xb[  0 ] == 11);
217         xi[  0 ] = 11; assert(xi[  0 ] == 11);
218         xj[J(0)] = 11; assert(xj[J(0)] == 11);
219         xe[ e0 ] = 11; assert(xe[ e0 ] == 11);
220         xs[XS.I(0)] = 11; assert(xs[XS.I(0)] == 11);
221         xs_[XS.I(0)] = 11; assert(xs_[XS.I(0)] == 11);
222 
223         // indexing with wrong type
224         static assert(!__traits(compiles, { xb[J(0)] = 11; }));
225         static assert(!__traits(compiles, { xi[J(0)] = 11; }));
226         static assert(!__traits(compiles, { xj[  0 ] = 11; }));
227         static assert(!__traits(compiles, { xe[  0 ] = 11; }));
228         static assert(!__traits(compiles, { xs[  0 ] = 11; }));
229         static assert(!__traits(compiles, { xs_[  0 ] = 11; }));
230 
231         import std.algorithm.comparison: equal;
232         import std.algorithm.iteration: filter;
233 
234         assert(equal(xb[].filter!(a => a < 11), [2, 3]));
235         assert(equal(xi[].filter!(a => a < 11), [2, 3]));
236         assert(equal(xj[].filter!(a => a < 11), [2, 3]));
237         assert(equal(xe[].filter!(a => a < 11), [2, 3]));
238         assert(equal(xs[].filter!(a => a < 11), [2, 3]));
239     }
240 }
241 
242 @safe pure nothrow unittest
243 {
244     auto x = [1, 2, 3];
245 
246     // sample index
247     struct Index(T = size_t)
248         if (isUnsigned!T)
249     {
250         this(T ix) { this._ix = ix; }
251         T opCast(U : T)() const { return _ix; }
252         private T _ix = 0;
253     }
254     alias J = Index!size_t;
255 
256     enum E { e0, e1, e2 }
257 
258     with (E)
259     {
260         auto xb = x.indexedBy!ubyte;
261         auto xi = x.indexedBy!uint;
262         auto xj = x.indexedBy!J;
263         auto xe = x.indexedBy!E;
264 
265         // indexing with correct type
266         xb[  0 ] = 11; assert(xb[  0 ] == 11);
267         xi[  0 ] = 11; assert(xi[  0 ] == 11);
268         xj[J(0)] = 11; assert(xj[J(0)] == 11);
269         xe[ e0 ] = 11; assert(xe[ e0 ] == 11);
270 
271         // slicing with correct type
272         xb[  0  ..   1 ] = 12; assert(xb[  0  ..   1 ] == [12]);
273         xi[  0  ..   1 ] = 12; assert(xi[  0  ..   1 ] == [12]);
274         xj[J(0) .. J(1)] = 12; assert(xj[J(0) .. J(1)] == [12]);
275         xe[ e0  ..  e1 ] = 12; assert(xe[ e0  ..  e1 ] == [12]);
276 
277         // indexing with wrong type
278         static assert(!__traits(compiles, { xb[J(0)] = 11; }));
279         static assert(!__traits(compiles, { xi[J(0)] = 11; }));
280         static assert(!__traits(compiles, { xj[  0 ] = 11; }));
281         static assert(!__traits(compiles, { xe[  0 ] = 11; }));
282 
283         // slicing with wrong type
284         static assert(!__traits(compiles, { xb[J(0) .. J(0)] = 11; }));
285         static assert(!__traits(compiles, { xi[J(0) .. J(0)] = 11; }));
286         static assert(!__traits(compiles, { xj[  0  ..   0 ] = 11; }));
287         static assert(!__traits(compiles, { xe[  0  ..   0 ] = 11; }));
288 
289         import std.algorithm.comparison: equal;
290         import std.algorithm.iteration: filter;
291 
292         assert(equal(xb.filter!(a => a < 11), [2, 3]));
293         assert(equal(xi.filter!(a => a < 11), [2, 3]));
294         assert(equal(xj.filter!(a => a < 11), [2, 3]));
295         assert(equal(xe.filter!(a => a < 11), [2, 3]));
296     }
297 }
298 
299 @safe pure nothrow unittest
300 {
301     auto x = [1, 2, 3];
302     struct I(T = size_t)
303     {
304         this(T ix) { this._ix = ix; }
305         T opCast(U : T)() const { return _ix; }
306         private T _ix = 0;
307     }
308     alias J = I!size_t;
309     auto xj = x.indexedBy!J;
310 }
311 
312 @safe pure nothrow unittest
313 {
314     auto x = [1, 2, 3];
315     struct I(T = size_t)
316     {
317         this(T ix) { this._ix = ix; }
318         private T _ix = 0;
319     }
320     alias J = I!size_t;
321     static assert(!__traits(compiles, { auto xj = x.indexedBy!J; }));
322 }
323 
324 @safe pure nothrow unittest
325 {
326     auto x = [1, 2, 3];
327     import bound: Bound;
328     alias B = Bound!(ubyte, 0, 2);
329     B b;
330     auto c = cast(size_t)b;
331     auto y = x.indexedBy!B;
332 }
333 
334 /** TODO shorter name */
335 enum StaticArrayOfElementTypeIndexedBy(E, I) = IndexedBy!(E[I.elementCountOf!E], I);