1 /**
2  * This module provides a single type, `SuperStruct`.
3  *
4  * Authors: Ryan Roden-Corrent ($(LINK2 https://github.com/rcorre, rcorre))
5  * License: MIT
6  * Copyright: © 2015, Ryan Roden-Corrent
7  */
8 module superstruct;
9 
10 /**
11  * It's a `struct`! It's a `class`! No, its ...  `SuperStruct`!
12  *
13  * This bastard child of
14  * $(LINK2 http://dlang.org/phobos/std_typecons.html#.wrap, wrap)
15  * and
16  * $(LINK2 http://dlang.org/phobos/std_variant.html, variant)
17  * works like an
18  * `Algebraic`, but exposes members that are common across the source types.
19  */
20 unittest {
21   import std.math, std.algorithm;
22 
23   struct Vector { float x, y; }
24 
25   struct Square {
26     float size;
27     Vector topLeft;
28 
29     auto area() { return size * size; }
30 
31     auto center() {
32       return Vector(topLeft.x + size / 2, topLeft.y + size / 2);
33     }
34 
35     auto center(Vector c) {
36       return topLeft = Vector(c.x - size / 2, c.y - size / 2);
37     }
38   }
39 
40   struct Circle {
41     float radius;
42     Vector center;
43     float area() { return radius * radius * PI; }
44   }
45 
46   // Shape may look like a class, but its actually a struct
47   alias Shape = SuperStruct!(Square, Circle);
48 
49   Shape sqr = Square(2, Vector(0,0));
50   Shape cir = Circle(4, Vector(0,0));
51   Shape[] shapes = [ sqr, cir ];
52 
53   // call functions that are shared between the source types:
54   assert(shapes.map!(x => x.area).sum.approxEqual(2 * 2 + 4 * 4 * PI));
55 
56   // It doesn't matter that `center` is a field of `Circle`, but a property of Square.
57   // They can be used in the same way:
58   cir.center = Vector(4, 2);
59   sqr.center = cir.center;
60   assert(sqr.center == Vector(4,2));
61 }
62 
63 /// SuperStruct forwards operators too:
64 unittest {
65   import std.range, std.algorithm, std.container;
66 
67   alias Container(T) = SuperStruct!(SList!T, Array!T);
68 
69   Container!int slist = SList!int();
70 
71   // We can call any members that are common among containers
72   slist.insert([1,2,3,4]);
73   assert(slist.front == 1);
74 
75   // opSlice is supported on all the subtypes, but each returns a different type
76   // Container.opSlice will return a SuperStruct of these types
77   auto slice = slist[];     // [1,2,3,4]
78   assert(slice.front == 1);
79   slice.popFront();         // [2,3,4]
80   assert(slice.front == 2);
81 
82   // as slice is a SuperStruct of range types, it still works as a range
83   slist.insert(slice); // [2,3,4] ~ [1,2,3,4]
84   assert(slist[].equal([2,3,4,1,2,3,4]));
85 }
86 
87 import std.meta;
88 import std.traits;
89 import std.variant;
90 
91 /** A Variant which exposes members that are common across all `SubTypes`.
92  *
93  * A `SuperStruct!(SubTypes...)` wraps an `Algebraic!(SubTypes...)`.  It can
94  * hold a single value from any of its `SubTypes`.
95  *
96  * Unlike a Variant/Algebraic, `SuperStruct` exposes access to 'common' members
97  * that have compatible signatures.
98  *
99  * A member is 'common' if its name describes a public function or field on
100  * every one of `SubTypes`. A call signature for a given member is 'compatible'
101  * if, for an instance of any one of `SubTypes`, that member can be called with
102  * the provided set of arguments _and_ all such calls have a common return type.
103  *
104  * `SuperStruct` ignores members beginning with "__" (double underscore).
105  */
106 struct SuperStruct(SubTypes...) {
107   private Algebraic!SubTypes _value;
108 
109   /**
110    * Construct and populate with an initial value.
111    * Params:
112    *   value = something implicitly covertible to of one of the `SubTypes`.
113    */
114   this(V)(V value) if (is(typeof(_value = value))) {
115     _value = value;
116   }
117 
118   /**
119    * Populate the `SuperStruct` with an instance of one of its sub-types.
120    * Params:
121    *   value = something implicitly covertible to of one of the `SubTypes`.
122    */
123   auto opAssign(V)(V value) if (is(typeof(_value = value))) {
124     return _value = value;
125   }
126 
127   /// Forward all members and template instantiations to the contained value.
128   mixin(allVisitorCode!SubTypes);
129 
130   // - Basic Operator Forwarding ---------------------------------
131 
132   /// Operators are forwarded to the underlying type.
133   auto opIndex(T...)(T t) { return _value.visitAny!(x => x[t]); }
134 
135   /// ditto
136   auto opSlice()() { return _value.visitAny!(x => x[]); }
137 
138   /// ditto
139   auto opSlice(A, B)(A a, B b) { return _value.visitAny!(x => x[a..b]); }
140 
141   /// ditto
142   auto ref opDollar(size_t pos)() {
143     auto ref dollar(T)(T val) {
144       static if (isArray!T)
145         return val.length;
146       else static if (is(typeof(val.opDollar!pos)))
147         return val.opDollar!pos();
148       else
149         return val.opDollar();
150     }
151 
152     return _value.visitAny!(x => dollar(x));
153   }
154 
155   /// ditto
156   auto opUnary(string op)() { return _value.visitAny!(x => mixin(op~"x")); }
157 
158   /// ditto
159   auto opBinary(string op, T)(T other) {
160     return _value.visitAny!(x => mixin("x"~op~"other"));
161   }
162 
163   /// ditto
164   auto opOpAssign(string op, T)(T other) {
165     return _value.visitAny!((ref x) => mixin("x"~op~"=other"));
166   }
167 
168   /// ditto
169   bool opEquals(T)(T other) { return _value.visitAny!(x => x == other); }
170 
171   /// ditto
172   auto opCmp(T)(T other) { return _value.visitAny!(x => x.opCmp(other)); }
173 
174   /// ditto
175   auto opCall(T...)(auto ref T args) { return _value.visitAny!(x => x(args)); }
176 
177   // - Operator Forwarding between SuperStructs -----------------
178 
179   /**
180    * Perform a binary operation between two superstructs.
181    *
182    * Only possible if such an operation is supported between any of the types
183    * in either of the SuperStructs.
184    */
185   auto opBinary(string op, T : SuperStruct!V, V...)(T other) {
186     return  _value.visitAny!(x =>
187       other._value.visitAny!(y => mixin("x"~op~"y")));
188   }
189 
190   /// ditto
191   auto opOpAssign(string op, T : SuperStruct!V, V...)(T other) {
192     return  _value.visitAny!((ref x) =>
193       other._value.visitAny!((    y) => mixin("x"~op~"=y")));
194   }
195 
196   /**
197    * Compare one `SuperStruct` to another of the same type.
198    *
199    * Invokes opEquals if the contained types are comparable.
200    * Otherwise returns false.
201    */
202   auto opEquals(typeof(this) other) {
203     bool helper(A, B)(A a, B b) {
204       static if (is(typeof(a == b)))
205         return a == b;
206       else
207         return false;
208     }
209 
210     return  _value.visitAny!(x =>
211       other._value.visitAny!(y => helper(x, y)));
212   }
213 
214   /**
215    * Extract the wrapped value of type `T` from the `SuperStruct`.
216    * Throws a `VariantException` if conversion to `T` is not possible.
217    */
218   auto opCast(T)() if (staticIndexOf!(T, SubTypes) >= 0) {
219     return _value.get!T;
220   }
221 }
222 
223 /// If all types have a matching field, it gets exposed:
224 unittest {
225   struct Foo { int a; }
226   struct Bar { int a; }
227   auto foobar = SuperStruct!(Foo, Bar)(Foo(1));
228   foobar.a = 5;
229   assert(foobar.a == 5);
230 }
231 
232 /// If all types have a matching method, all compatible overloads are exposed:
233 unittest {
234   struct Foo {
235     int fun(int i) { return i; }
236     int fun(int a, int b) { return a + b; }
237   }
238   struct Bar {
239     int fun(int i) { return i; }
240     int fun(int a, int b) { return a + b; }
241     int fun(int a, int b, int c) { return a + b + c; }
242   }
243 
244   auto foobar = SuperStruct!(Foo, Bar)(Foo());
245   assert(foobar.fun(1)    == 1);
246   assert(foobar.fun(1, 2) == 3);
247   assert(!__traits(compiles, foobar.fun(1,2,3))); // no such overload on Foo
248 }
249 
250 /// If a name is a field on one type and a method on another, it is exposed:
251 unittest {
252   struct Foo { int a; }
253   struct Bar {
254     private int _a;
255     int a() { return _a; }
256     int a(int val) { return _a = val; }
257   }
258 
259   auto foo = SuperStruct!(Foo, Bar)(Foo());
260   foo.a = 5;          // sets Foo.a
261   assert(foo.a == 5); // gets Foo.a
262 
263   auto bar = SuperStruct!(Foo, Bar)(Bar());
264   bar.a = 5;          // invokes Bar.a(int val)
265   assert(bar.a == 5); // invokes Bar.a()
266 }
267 
268 /// Templated members can be forwarded too:
269 unittest {
270   struct Foo {
271     int val;
272     auto transmorgrify(alias fn1, alias fn2)() {
273       return fn2(fn2(val));
274     }
275   }
276 
277   struct Bar {
278     auto transmorgrify(alias fn1, alias fn2)() { return 0; }
279   }
280 
281   static auto add1 = (int a) => a + 1;
282 
283   alias FooBar = SuperStruct!(Foo, Bar);
284 
285   FooBar f = Foo(3);
286   assert(f.transmorgrify!(add1, add1) == 5); // 3 + 1 + 1
287 
288   FooBar b = Bar();
289   assert(b.transmorgrify!(add1, add1) == 0);
290 }
291 
292 /// Operators get forwarded to the underlying type
293 unittest {
294   struct Foo {
295     auto opSlice() { return [1,2,3]; }
296   }
297 
298   struct Bar {
299     auto opSlice() { return [4,5,6]; }
300   }
301 
302   SuperStruct!(Foo, Bar) fb = Foo();
303   assert(fb[] == [1,2,3]);
304 }
305 
306 /// SuperStructs of the same type can be compared:
307 unittest {
308   struct A { int i; }
309   struct B { int i; }
310   struct C { int i; bool opEquals(T)(T other) { return other.i == i; } }
311 
312   SuperStruct!(A, B, C) a0 = A(0);
313   SuperStruct!(A, B, C) a1 = A(1);
314   SuperStruct!(A, B, C) b0 = B(0);
315   SuperStruct!(A, B, C) c0 = C(0);
316 
317   assert(a0 == a0); // same type, same value
318   assert(a0 != a1); // same type, different value
319   assert(a0 != b0); // incomparable types return false
320   assert(a0 == c0); // different but comparable types
321 
322   // SuperStructs with different sets of source types are not comparable, even
323   // if the types they happen to contain at the moment are.
324   SuperStruct!(A, B) different = A(0);
325   static assert(!__traits(compiles, different == a0));
326 }
327 
328 /// If members have common signatures but no common return type, the exposed
329 /// member returns a `SuperStruct` of the possible return types.
330 unittest {
331   struct A { auto fun() { return 1; } }
332   struct B { auto fun() { return "hi"; } }
333 
334   SuperStruct!(A, B) a = A();
335   SuperStruct!(A, B) b = B();
336 
337   assert(a.fun == SuperStruct!(int, string)(1));
338   assert(b.fun == SuperStruct!(int, string)("hi"));
339 }
340 
341 /// Use `cast(T)` to convert a `SuperStruct` to a subtype
342 unittest {
343   import std.exception : assertThrown;
344 
345   struct Square { float size; }
346   struct Circle { float r; }
347 
348   SuperStruct!(Square, Circle) sqr = Square(4);
349   SuperStruct!(Square, Circle) cir = Circle(6);
350 
351   assert((cast(Square) sqr).size == 4);
352   assert((cast(Circle) cir).r    == 6);
353 
354   assertThrown!VariantException(cast(Circle) sqr);
355   assertThrown!VariantException(cast(Square) cir);
356 }
357 
358 /**
359  * Wrap one of two values in a `SuperStruct` that encompasses both types.
360  *
361  * This can be used to return a value from one of several different types.
362  * Similar to `std.range.chooseAmong`, but for a broader range of types.
363  *
364  * Params:
365  *  condition = which value to choose: $(D a) if $(D true), $(D b) otherwise
366  *  a = the "true" value
367  *  b = the "false" value
368  *
369  * Returns: A `SuperStruct!(T1,T2)` wrapping the selected value.
370  */
371 auto pick(T1, T2)(bool condition, T1 a, T2 b) {
372   if (condition) return SuperStruct!(T1, T2)(a);
373   else           return SuperStruct!(T1, T2)(b);
374 }
375 
376 ///
377 unittest {
378   struct Square { int size; int area() { return size * size; } }
379   struct Rect   { int w ,h; int area() { return w * h; } }
380 
381   auto shape(int w, int h) {
382     return pick(w == h, Square(w), Rect(w,h));
383   }
384 
385   assert(shape(4,5).area == 20);
386   assert(shape(3,3).area == 9);
387 }
388 
389 /// `pick` is useful for something that is a floor wax _and_ a dessert topping:
390 unittest {
391   struct FloorWax       { string itIs() { return "a floor wax!";       } }
392   struct DessertTopping { string itIs() { return "a dessert topping!"; } }
393 
394   auto shimmer(bool hungry) {
395     return pick(hungry, DessertTopping(), FloorWax());
396   }
397 
398   assert(shimmer(false).itIs == "a floor wax!");
399   assert(shimmer(true ).itIs == "a dessert topping!");
400 }
401 
402 /**
403  * Wrap one of two values in a `SuperStruct` that encompasses both types.
404  *
405  * This can be used to return a value from one of several different types.
406  * Similar to `std.range.chooseAmong`, but for a broader range of types.
407  *
408  * Params:
409  *  index = which value to choose, must be less than the number of values
410  *  values = two or more values to pick from
411  *
412  * Returns: A `SuperStruct!T` wrapping the argument at `index`.
413  */
414 auto pickAmong(T...)(size_t index, T values) {
415   foreach(idx, val ; values)
416     if (idx == index)
417       return SuperStruct!T(val);
418 
419   assert(0, "index not in range of provided values");
420 }
421 
422 /// use `pickAmong` to form a common return type from different structs
423 unittest {
424   struct Zero  { auto num = 0; }
425   struct One   { auto num = 1; }
426   struct Two   { auto num = 2; }
427 
428   auto val = pickAmong(0, Zero(), One(), Two());
429   assert(val.num == 0);
430 
431   val = pickAmong(1, Zero(), One(), Two());
432   assert(val.num == 1);
433 
434   val = pickAmong(2, Zero(), One(), Two());
435   assert(val.num == 2);
436 }
437 
438 /// `pickAmong` is similar to `std.range.chooseAmong`:
439 unittest {
440   import std.range : only, iota, repeat;
441   import std.algorithm : equal;
442 
443   auto r = pickAmong(0, only(1), iota(0,3), repeat(1,3));
444   assert(r.equal([1]));
445 
446   r = pickAmong(1, only(1), iota(0,3), repeat(1,3));
447   assert(r.equal([0,1,2]));
448 
449   r = pickAmong(2, only(1), iota(0,3), repeat(1,3));
450   assert(r.equal([1,1,1]));
451 }
452 
453 private:
454 /*
455  * Try to invoke `member` with the provided `args` for every `AllowedType`.
456  * Compiles only if such a call is possible on every type.
457  * Compiles only if the return values of all such calls share a common type.
458  */
459 template visitMember(string member, TemplateArgs...) {
460   // if we got some explicit compile time args, we need to pass them along.
461   // otherwise, omit the !() as an empty template arg list can cause issues
462   static if (TemplateArgs.length)
463     enum bang = "!(TemplateArgs)";
464   else
465     enum bang = "";
466 
467   // this nested function allows two sets of compile-time args:
468   // one from the enclosing template scope and one for the variadic args of
469   auto visitMember(V, Args...)(ref V var, Args args) {
470     static if (Args.length == 0)      // field or 'getter' (no-args function)
471       enum expression = "x."~member~bang;
472     else static if (Args.length == 1) // field or 'setter' (1-arg function)
473       enum expression = "x."~member~bang~"=args[0]";
474     else                              // 2+ arg function
475       enum expression = "x."~member~bang~"(args)";
476 
477     return var.visitAny!((ref x) => mixin(expression));
478   }
479 }
480 
481 unittest {
482   struct Foo {
483     int num;
484 
485     string shout() { return "hi!"; }
486     void assign(int val) { num = val; }
487     void assign(int val1, int val2) { num = val1 + val2; }
488 
489     void set(T)(T val) { num = val; } // templated setter
490   }
491 
492   struct Bar {
493     int num, othernum;
494 
495     string shout() { return "bye!"; }
496     void assign(int val) { num = val + 1; }
497     void assign(int val1, int val2) { num = val1 + val2 + 1; }
498     void assign(int val1, int val2, int val3) { num = val1 + val2 + val3; }
499 
500     void set(T)(T val) { num = val; }
501   }
502 
503   alias Thing = Algebraic!(Foo, Bar);
504 
505   Thing foo = Foo(4);
506   Thing bar = Bar(5, 6);
507 
508   assert(visitMember!"num"(foo) == 4);
509   assert(visitMember!"num"(bar) == 5);
510 
511   assert(visitMember!"num"(foo, 5) == 5);
512   assert(visitMember!"num"(foo)    == 5);
513 
514   assert(visitMember!"shout"(foo) == "hi!");
515   assert(visitMember!"shout"(bar) == "bye!");
516   assert(visitMember!"shout"(bar) == "bye!");
517 
518   visitMember!"assign"(foo, 2);
519   assert(visitMember!"num"(foo) == 2);
520 
521   visitMember!"assign"(bar, 2);
522   assert(visitMember!"num"(bar) == 3); // bar adds 1
523 
524   visitMember!"assign"(foo, 2, 6);
525   assert(visitMember!"num"(foo) == 8);
526 
527   visitMember!"set"(foo, 9);
528   assert(visitMember!"num"(foo) == 9);
529 
530   // field 'othernum' only exists on bar
531   static assert(!__traits(compiles, visitMember!"othernum"(bar)));
532   static assert(!__traits(compiles, visitMember!"othernum"(bar)));
533 
534   // 3-param overload of 'assign' only exists on Bar
535   static assert(!__traits(compiles, visitMember!"assign"(bar, 2, 6, 8)));
536 }
537 
538 // pass along template arguments
539 unittest {
540   struct Foo {
541     int val;
542 
543     auto noargs(alias fn)() { return fn(val); }
544     auto onearg(alias fn)(int i) { return fn(i);   }
545     auto twofns(alias fn1, alias fn2)(int i) { return fn2(fn1(i)); }
546 
547     auto onetype(T)(T arg) { return val + arg; }
548     auto twotype(T, V)(T t, V v) { return val + t + v; }
549   }
550 
551   struct Bar {
552     int val;
553 
554     auto noargs(alias fn)() { return fn(val); }
555     auto onearg(alias fn)(int i) { return fn(i);   }
556     auto twofns(alias fn1, alias fn2)(int i) { return fn2(fn1(i)); }
557 
558     auto onetype(T)(T arg) { return val + arg; }
559     auto twotype(T, V)(T t, V v) { return val + t + v; }
560   }
561 
562   alias FooBar = Algebraic!(Foo, Bar);
563   FooBar fb = Foo(3);
564 
565   // need to use a static fn here due to unrelated issue:
566   // cannot use local 'add1' as parameter to non-global template
567   static auto add1 = (int a) => a + 1;
568   static auto add2 = (int a) => a + 2;
569 
570   assert(fb.visitMember!("noargs", add1)()        == 4); // 3 + 1
571   assert(fb.visitMember!("onearg", add1)(5)       == 6); // 5 + 1
572   assert(fb.visitMember!("twofns", add1, add2)(5) == 8); // 5 + 1 + 2
573 
574   // implicit type args
575   assert(fb.visitMember!("onetype")(5)      == 8);   // 3 + 5
576   assert(fb.visitMember!("twotype")(5, 7)   == 15);  // 3 + 5 + 7
577   assert(fb.visitMember!("twotype")(5f, 7f) == 15f); // 3 + 5 + 7
578 
579   // explicit type args
580   assert(fb.visitMember!("onetype", int)(5)             == 8);   // 3 + 5
581   assert(fb.visitMember!("twotype", int)(5, 7)          == 15);  // 3 + 5 + 7
582   assert(fb.visitMember!("twotype", float, float)(5, 7) == 15f); // 3 + 5 + 7
583 
584   // only specify some type args
585   assert(fb.visitMember!("twotype", float)(5, 7) == 15f); // 3 + 5 + 7
586 }
587 
588 auto visitAny(alias fn, V)(ref V var) {
589   // Collect the possible return types for this function across the subtypes
590   alias returnType(T) = typeof(fn(*var.peek!T));
591   alias AllTypes      = staticMap!(returnType, V.AllowedTypes);
592 
593   enum allVoid   = EraseAll!(void, AllTypes).length == 0;
594   enum allCommon = !is(CommonType!AllTypes == void);
595 
596   foreach(T ; var.AllowedTypes)
597     if (auto ptr = var.peek!T) {
598       static if (allCommon || allVoid)
599         return fn(*ptr);
600       else static if (!allVoid)
601         return SuperStruct!AllTypes(fn(*ptr));
602       else
603         static assert(0, "Cannot mix void and non-void return types");
604     }
605 
606   assert(0, "Variant holds no value");
607 }
608 
609 unittest {
610   struct Foo {
611     auto opSlice() { return [1,2,3]; }
612     auto opBinary(string op)(string val) { return "foo"~op~"val"; }
613   }
614 
615   struct Bar {
616     auto opSlice() { return [4,5,6]; }
617     auto opBinary(string op)(string val) { return "foo"~op~"val"; }
618   }
619 
620   Algebraic!(Foo, Bar) fb = Foo();
621   assert(fb.visitAny!(x => x[]) == [1,2,3]);
622 }
623 
624 unittest {
625   struct One { auto opEquals(int i) { return i == 1; } }
626   struct Two { auto opEquals(int i) { return i == 2; } }
627 
628   Algebraic!(One, Two) one = One();
629   Algebraic!(One, Two) two = Two();
630 
631   assert( one.visitAny!(x => x == 1));
632   assert(!one.visitAny!(x => x == 2));
633   assert(!two.visitAny!(x => x == 1));
634   assert( two.visitAny!(x => x == 2));
635 }
636 
637 /*
638  * Generate a templated function to expose access to a given member across all
639  * types that could be stored in the Variant `_value`.
640  * For any given call signature, this template will instantiate only if the
641  * matching member on every subtype is callable with such a signature _and_ if
642  * all such calls have a common return type.
643  */
644 string memberVisitorCode(string name)() {
645   import std.string : format;
646 
647   return q{
648     template %s(TemplateArgs...) {
649       auto %s(Args...)(Args args) {
650         return visitMember!("%s", TemplateArgs)(_value, args);
651       }
652     }
653   }.format(name, name, name);
654 }
655 
656 unittest {
657   struct Foo {
658     int a;
659     int b;
660     int c;
661     int d;
662     int e;
663   }
664 
665   struct Bar {
666     int    a;
667     real   b;
668     int    c() { return 1; }        // getter only
669     string d;                       // incompatible type
670     int    e(int val) { return 0; } // setter only
671   }
672 
673   struct FooBar {
674     alias Store = Algebraic!(Foo, Bar);
675     Store _value;
676 
677     this(T)(T t) { _value = t; }
678 
679     mixin(memberVisitorCode!("a"));
680     mixin(memberVisitorCode!("b"));
681     mixin(memberVisitorCode!("c"));
682     mixin(memberVisitorCode!("d"));
683     mixin(memberVisitorCode!("e"));
684   }
685 
686   FooBar fb = Foo(1);
687 
688   static assert(is(typeof(fb.a()) == int));  // both are int
689   static assert(is(typeof(fb.b()) == real)); // real is common type of (int, real)
690   static assert(is(typeof(fb.c()) == int));  // field on Foo, function on Bar
691 
692   static assert( is(typeof(fb.a = 5) == int )); // both are int
693   static assert( is(typeof(fb.b = 5) == real)); // real is common type of (int, real)
694   static assert( is(typeof(fb.e = 5) == int )); // field on Foo, function on Bar
695 
696   static assert(!is(typeof(fb.b = 5.0))); // type mismatch
697   static assert(!is(typeof(fb.c = 5  ))); // getter only
698   static assert(!is(typeof(fb.d = 5  ))); // incompatible types
699   static assert(!is(typeof(fb.d = 5.0))); // incompatible types
700 }
701 
702 /*
703  * Generate a string containing the `memberVisitorCode` for every name in the
704  * union of all members across SubTypes.
705  */
706 string allVisitorCode(SubTypes...)() {
707   // allMembers will fail on a primitive types, so alias that to an empty list
708   template allMembers(T) {
709     static if (__traits(compiles, __traits(allMembers, T)))
710       alias allMembers = AliasSeq!(__traits(allMembers, T));
711     else
712       alias allMembers = AliasSeq!();
713   }
714 
715   // ignore hidden members like __ctor, this, and operators
716   enum shouldExpose(string name) = (name.length < 2 || name[0..2] != "__") &&
717                                    name != "this"     &&
718                                    name != "opUnary"  &&
719                                    name != "opBinary" &&
720                                    name != "opCast"   &&
721                                    name != "opEquals" &&
722                                    name != "opCmp"    &&
723                                    name != "opCall"   &&
724                                    name != "opAssign" &&
725                                    name != "opIndex"  &&
726                                    name != "opSlice"  &&
727                                    name != "opDollar";
728 
729   // we _could_ generate members that will never instantiate, but might as well
730   // filter out ones that we _know_ don't exist on at least one subtype
731   template allHave(string member) {
732     enum have(T) = hasMember!(T, member);
733     enum allHave = allSatisfy!(have, SubTypes);
734   }
735 
736   string str;
737 
738   // generate a member to forward to each underlying member
739   foreach(member ; NoDuplicates!(staticMap!(allMembers, SubTypes)))
740     static if (shouldExpose!member && allHave!member)
741       str ~= memberVisitorCode!(member);
742 
743   return str;
744 }
745 
746 unittest {
747   struct Foo {
748     int a;
749     int b;
750     int c;
751     int d;
752     int e;
753   }
754 
755   struct Bar {
756     int    a;
757     real   b;
758     int    c() { return 1; }        // getter only
759     string d;                       // incompatible type
760     int    e(int val) { return 0; } // setter only
761   }
762 
763   struct FooBar {
764     alias Store = Algebraic!(Foo, Bar);
765     Store _value;
766 
767     this(T)(T t) { _value = t; }
768 
769     mixin(allVisitorCode!(Foo, Bar));
770   }
771 
772   FooBar fb = Foo(1);
773 
774   // getters
775   static assert( is(typeof(fb.a()) == int));  // both are int
776   static assert( is(typeof(fb.b()) == real)); // real is common type of (int, real)
777   static assert( is(typeof(fb.c()) == int));  // field on Foo, function on Bar
778   static assert( is(typeof(fb.d()) == SuperStruct!(int, string)));
779   static assert(!is(typeof(fb.e())));  // setter only
780 
781   // setters
782   static assert( is(typeof(fb.a = 5) == int )); // both are int
783   static assert( is(typeof(fb.b = 5) == real)); // real is common type of (int, real)
784   static assert( is(typeof(fb.e = 5) == int )); // field on Foo, function on Bar
785 
786   static assert(!is(typeof(fb.b = 5.0))); // type mismatch
787   static assert(!is(typeof(fb.c = 5  ))); // getter only
788   static assert(!is(typeof(fb.d = 5  ))); // incompatible types
789   static assert(!is(typeof(fb.d = 5.0))); // incompatible types
790 }
791 
792 // make sure __ctor and __dtor don't blow things up
793 unittest {
794   struct Foo {
795     this(int i) { }
796     this(this) { }
797     ~this() { }
798   }
799 
800   struct Bar {
801     this(int i) { }
802     this(this) { }
803     ~this() { }
804   }
805 
806   struct FooBar {
807     alias Store = Algebraic!(Foo, Bar);
808     Store _value;
809 
810     this(T)(T t) { _value = t; }
811 
812     mixin(allVisitorCode!(Foo, Bar));
813   }
814 
815   FooBar fb = Foo(1);
816 }