1 module ut.polymorphic;
2 
3 
4 import ut;
5 
6 
7 private interface ITransformer {
8     int transform(int) @safe pure const;
9 }
10 
11 private alias Transformer = Polymorphic!ITransformer;
12 
13 private interface INonThrower {
14     import std.traits: FA = FunctionAttribute;
15     enum CopyConstructorAttrs = FA.safe | FA.pure_ | FA.nothrow_;
16     enum DestructorAttrs = FA.safe | FA.pure_ |  FA.nothrow_;
17     
18     int transform(int) @safe pure nothrow const;
19 }
20 
21 private alias NonThrower = Polymorphic!INonThrower;
22 
23 private int xform(in Transformer t, int i) @safe /* pure */ {
24     return t.transform(i);
25 }
26 
27 private int xform(in NonThrower t, int i) @safe pure nothrow {
28     return t.transform(i);
29 }
30 
31 
32 @("struct.stateless.Twice")
33 @safe unittest {
34 
35     static struct Twice {
36         int transform(int i) @safe pure const { return i * 2; }
37     }
38 
39     const twice = Transformer(Twice());
40     xform(twice, 1).should == 2;
41     xform(twice, 2).should == 4;
42     xform(twice, 3).should == 6;
43 }
44 
45 
46 @("struct.stateless.Thrice")
47 @safe unittest {
48 
49     static struct Thrice {
50         int transform(int i) @safe pure const { return i * 3; }
51     }
52 
53     const thrice = Transformer(Thrice());
54     xform(thrice, 1).should == 3;
55     xform(thrice, 2).should == 6;
56     xform(thrice, 3).should == 9;
57 }
58 
59 @("struct.stateless.Nothrow")
60 @safe unittest {
61     const testValues = () nothrow {
62         static struct Square {
63             int transform(int i) @safe pure nothrow const { return i * i; }
64         }
65         const NonThrower square = Square();
66         return [xform(square, 1), xform(square, 2), xform(square, 3)];
67     }();
68 
69     testValues.should == [1, 4, 9];
70 }
71 
72 
73 @("struct.stateless.lib")
74 @safe unittest {
75     import modules.types: Negative;
76     const negative = Transformer(Negative());
77     xform(negative, 1).should == -1;
78     xform(negative, 2).should == -2;
79     xform(negative, 3).should == -3;
80 }
81 
82 
83 @("struct.stateful.Multiplier")
84 @safe unittest {
85 
86     static struct Multiplier {
87         int i;
88         int transform(int j) @safe pure const { return i * j; }
89     }
90 
91     xform(Transformer(Multiplier(2)), 3).should == 6;
92     xform(Transformer(Multiplier(2)), 4).should == 8;
93     xform(Transformer(Multiplier(3)), 3).should == 9;
94     xform(Transformer(Multiplier(3)), 4).should == 12;
95 }
96 
97 
98 @("class.stateless.Thrice")
99 @safe unittest {
100 
101     static class Thrice {
102         int transform(int i) @safe pure const { return i * 3; }
103     }
104 
105     const thrice = Transformer(new Thrice());
106     xform(thrice, 1).should == 3;
107     xform(thrice, 2).should == 6;
108     xform(thrice, 3).should == 9;
109 }
110 
111 
112 @("class.stateful.Multiplier")
113 @safe unittest {
114 
115     static class Multiplier {
116         int i;
117         this(int i) @safe pure { this.i = i; }
118         this(const Multiplier other) @safe pure { this.i = other.i; }
119         int transform(int j) @safe pure const { return i * j; }
120     }
121 
122     xform(Transformer(new Multiplier(2)), 3).should == 6;
123     xform(Transformer(new Multiplier(2)), 4).should == 8;
124     xform(Transformer(new Multiplier(3)), 3).should == 9;
125     xform(Transformer(new Multiplier(3)), 4).should == 12;
126 }
127 
128 
129 @("template")
130 @safe unittest {
131     import modules.ufcs.template_: Struct;
132 
133     xform(Transformer.create!"modules.ufcs.template_"(Struct(2)), 3).should == 5;
134     xform(Transformer.create!"modules.ufcs.template_"(Struct(2)), 4).should == 6;
135 
136     xform(Transformer.create!"modules.ufcs.template_"(2), 3).should == 6;
137     xform(Transformer.create!"modules.ufcs.template_"(2), 4).should == 8;
138 
139     xform(Transformer.create!"modules.ufcs.template_"(4.0), 3).should == 1;
140     xform(Transformer.create!"modules.ufcs.template_"(5.0), 3).should == 2;
141 }
142 
143 
144 @("scalar.pointer.transform.int.standard")
145 @safe unittest {
146     auto three = Transformer.create!"modules.ufcs.pointer.transform"(3);
147     xform(three, 2).should == 6;
148     xform(three, 3).should == 9;
149 }
150 
151 @("scalar.pointer.transform.int.extra")
152 @safe unittest {
153     auto four = Transformer.create!(
154              "modules.ufcs.pointer.transform",
155              // pass in a module that has nothing to with anything to test
156              // that it still works if there's no function with that
157              // name in it
158              "modules.types",
159         )
160         (4);
161     xform(four, 2).should == 8;
162     xform(four, 3).should == 12;
163 }
164 
165 
166 @("scalar.pointer.transform.double")
167 @safe unittest {
168     auto double_ = Transformer.create!"modules.ufcs.pointer.transform"(3.3);
169     xform(double_, 2).should == 5;
170     xform(double_, 3).should == 6;
171     xform(double_, 4).should == 7;
172 }
173 
174 
175 @("scalar.value.transform.double")
176 @safe unittest {
177     auto double_ = Transformer.create!"modules.ufcs.value.transform"(3.3);
178     xform(double_, 2).should == 5;
179     xform(double_, 3).should == 6;
180     xform(double_, 4).should == 7;
181 }
182 
183 
184 @("scalar.ref.transform.double")
185 @safe unittest {
186     auto double_ = Transformer.create!"modules.ufcs.ref_.transform"(3.3);
187     xform(double_, 2).should == 5;
188     xform(double_, 3).should == 6;
189     xform(double_, 4).should == 7;
190 }
191 
192 
193 @("scalar.value.transform.string")
194 @safe unittest {
195     const string_ = Transformer.create!("modules.ufcs.value.transform")("foobar");
196     xform(string_, 2).should == 8;
197     xform(string_, 3).should == 9;
198 }
199 
200 
201 @("scalar.ref.transform.string")
202 @safe unittest {
203     const string_ = Transformer.create!("modules.ufcs.ref_.transform")("foobar");
204     xform(string_, 2).should == 8;
205     xform(string_, 3).should == 9;
206 }
207 
208 
209 @("array.int")
210 @safe /* pure FIXME */ unittest {
211     static import modules.ufcs.pointer.stringify;
212     import modules.types: Negative, Point, String;
213     import std.algorithm.iteration: map;
214     import std.array: array;
215 
216     static interface IPrintable {
217         string stringify() @safe pure const;
218     }
219 
220     alias Printable = Polymorphic!IPrintable;
221 
222     auto printable = Printable.create!(modules.ufcs.pointer.stringify)(42);
223     printable.stringify.should == "42";
224 }
225 
226 
227 
228 @("array.string")
229 @safe unittest {
230     static import modules.ufcs.pointer.stringify;
231     import modules.types: Negative, Point, String;
232     import std.algorithm.iteration: map;
233     import std.array: array;
234 
235     static interface IPrintable {
236         string stringify() @safe const;
237     }
238 
239     alias Printable = Polymorphic!IPrintable;
240 
241     auto printables = [
242         Printable.create!(modules.ufcs.pointer.stringify)("foobar"),
243     ];
244 
245     printables.map!(a => a.stringify).array.should == [
246         "foobar",
247     ];
248 }
249 
250 
251 @("array.safe")
252 @safe unittest {
253     static import modules.ufcs.pointer.stringify;
254     import modules.types: Negative, Point, String;
255     import std.algorithm.iteration: map;
256     import std.array: array;
257 
258     static interface IPrintable {
259         string stringify() @safe const;
260     }
261 
262     alias Printable = Polymorphic!IPrintable;
263 
264     auto printables = [
265         Printable.create!(modules.ufcs.pointer.stringify)(42),
266         Printable.create!(modules.ufcs.pointer.stringify)(3.3),
267         Printable.create!(modules.ufcs.pointer.stringify)("foobar"),
268         Printable.create!(modules.ufcs.pointer.stringify)(String("quux")),
269         Printable.create!(modules.ufcs.pointer.stringify)(Negative()),
270         Printable.create!(modules.ufcs.pointer.stringify)(Point(2, 3)),
271     ];
272 
273     // the conversion to an array is to maintain @safeness
274     // (don't ask)
275     printables.map!(a => a.stringify).array.should == [
276         "42",
277         "3.3",
278         "foobar",
279         "quux",
280         "Negative",
281         "Point(2, 3)",
282     ];
283 }
284 
285 
286 @("array.system")
287 @system unittest {
288     static import modules.ufcs.pointer.stringify;
289     import modules.types: Negative, Point, String;
290     import std.algorithm.iteration: map;
291 
292     static interface IPrintable {
293         string stringify() @system const;
294     }
295 
296     alias Printable = Polymorphic!IPrintable;
297 
298     auto printables = [
299         Printable.create!(modules.ufcs.pointer.stringify)(42),
300         Printable.create!(modules.ufcs.pointer.stringify)(3.3),
301         Printable.create!(modules.ufcs.pointer.stringify)("foobar"),
302         Printable.create!(modules.ufcs.pointer.stringify)(String("quux")),
303         Printable.create!(modules.ufcs.pointer.stringify)(Negative()),
304         Printable.create!(modules.ufcs.pointer.stringify)(Point(2, 3)),
305     ];
306 
307     printables.map!(a => a.stringify).should == [
308         "42",
309         "3.3",
310         "foobar",
311         "quux",
312         "Negative",
313         "Point(2, 3)",
314     ];
315 }
316 
317 
318 @("array.xform")
319 // not pure because the copy constructor isn't
320 @safe unittest {
321     static struct Twice {
322         int transform(int i) @safe /* pure */ const { return i * 2; }
323     }
324     static struct Multiplier {
325         int i;
326         int transform(int j) @safe /* pure */ const { return i * j; }
327     }
328 
329     xform(Transformer(Twice()), 1).should == 2;
330 
331     auto xformers = [ Transformer(Twice()), Transformer(Multiplier(3)) ];
332     xform(xformers[0], 1).should == 2;
333     xform(xformers[0], 2).should == 4;
334     xform(xformers[1], 1).should == 3;
335     xform(xformers[1], 2).should == 6;
336 }
337 
338 
339 @("self.immutable")
340 @safe /* pure */ unittest {
341     static interface Interface0 {
342         int fun() @safe pure immutable;
343     }
344     alias Poly = Polymorphic!Interface0;
345 
346     static struct Mutable {
347         int fun() @safe pure { return 0; }
348     }
349 
350     static struct Const {
351         int fun() @safe pure const { return 1; }
352     }
353 
354     static struct Immutable {
355         int fun() @safe pure immutable { return 2; }
356     }
357 
358     static assert(!__traits(compiles, Poly(Mutable())));
359 
360     const c = Poly(Const());
361     static assert(!__traits(compiles, c.fun()));
362 
363     immutable i = immutable Poly(Immutable());
364     i.fun.should == 2;
365 }
366 
367 
368 @("storageClass")
369 @safe /* pure */ unittest {
370 
371     static interface Interface1 {
372         void storageClasses(
373             int normal,
374             return scope int* returnScope,
375             out int out_,
376             ref int ref_,
377             lazy int lazy_,
378         );
379     }
380 
381     alias Poly = Polymorphic!Interface1;
382 
383     static assert(is(typeof(Poly.storageClasses) == typeof(Interface1.storageClasses)));
384 }
385 
386 
387 @("dtor")
388 @safe unittest {
389 
390     static interface Interface2 {
391         int value() @safe @nogc pure const;
392     }
393     alias Poly = Polymorphic!Interface2;
394 
395     static struct Id {
396         static int numIds;
397         int i;
398 
399         @disable this();
400 
401         this(int i) {
402             writelnUt(&this, " ctor");
403             this.i = i;
404             ++numIds;
405         }
406 
407         this(ref scope const Id other) {
408             writelnUt(&this, " copy ctor");
409             i = other.i;
410             ++numIds;
411         }
412 
413         ~this() inout {
414             writelnUt(&this, " dtor");
415             --numIds;
416         }
417 
418         int value() @safe @nogc pure const { return i; }
419     }
420 
421     Id.numIds.should == 0;
422     {
423         const id0 = Poly(Id(42));
424         Id.numIds.should == 1;
425         id0.value.should == 42;
426 
427         const id1 = Poly(Id(33));
428         Id.numIds.should == 2;
429         id1.value.should == 33;
430     }
431     Id.numIds.should == 0;
432 }
433 
434 
435 @("defaultValues")
436 @safe /* pure */ unittest {
437 
438     static interface Interface3 {
439         string fun(int i, int j = 1, int k = 2) @safe pure scope const;
440     }
441     alias Poly = Polymorphic!Interface3;
442 
443     static struct Struct {
444         string fun(int i, int j, int k) @safe pure scope const {
445             import std.conv: text;
446             return text("i: ", i, " j: ", j, " k: ", k);
447         }
448     }
449 
450     auto obj = Poly(Struct());
451     obj.fun(2, 3, 4).should == "i: 2 j: 3 k: 4";
452     obj.fun(4, 3).should == "i: 4 j: 3 k: 2";
453     obj.fun(5).should == "i: 5 j: 1 k: 2";
454 }
455 
456 
457 @("struct.stateless.pure")
458 @safe pure unittest {
459 
460     static interface Interface4 {
461         import std.traits: FA = FunctionAttribute;
462 
463         enum CopyConstructorAttrs = FA.safe | FA.pure_;
464         enum DestructorAttrs = FA.safe | FA.pure_;
465 
466         int transform(int) @safe pure const;
467     }
468     alias Poly = Polymorphic!Interface4;
469 
470     static struct Struct {
471         int transform(int i) @safe pure const { return i * 2; }
472     }
473 
474     const s = Poly(Struct());
475     const copy = s;
476 }
477 
478 
479 @("struct.stateful.overload")
480 @safe pure unittest {
481     static interface Interface5 {
482         import std.traits: FA = FunctionAttribute;
483         enum DestructorAttrs = FA.safe | FA.pure_;
484         int calc(int) @safe pure const;
485         int calc(int, int) @safe pure const;
486     }
487     alias Poly = Polymorphic!Interface5;
488 
489     static struct Adder {
490         int i;
491         int calc(int j) @safe pure const { return i + j; }
492         int calc(int j, int k) @safe pure const { return i + j + k; }
493     }
494 
495     const poly = Poly(Adder(3));
496 
497     poly.calc(1).should == 4;
498     poly.calc(2).should == 5;
499 
500     poly.calc(1, 2).should == 6;
501     poly.calc(1, 3).should == 7;
502 }
503 
504 
505 @("struct.stateful.create")
506 @safe pure unittest {
507     static interface Interface6 {
508         import std.traits: FA = FunctionAttribute;
509         enum DestructorAttrs = FA.safe | FA.pure_;
510         int calc(int) @safe pure const;
511     }
512     alias Poly = Polymorphic!Interface6;
513 
514     static struct Adder {
515         int i;
516         int calc(int j) @safe pure const { return i + j; }
517     }
518 
519     const poly = Poly.create!Adder(3);
520     poly.calc(1).should == 4;
521     poly.calc(2).should == 5;
522 }