Construct and populate with an initial value.
ditto ditto
Perform a binary operation between two superstructs.
ditto ditto
Compare one SuperStruct to another of the same type.
Operators are forwarded to the underlying type.
ditto ditto
Perform a binary operation between two superstructs.
Operators are forwarded to the underlying type.
ditto ditto
If all types have a matching field, it gets exposed:
struct Foo { int a; } struct Bar { int a; } auto foobar = SuperStruct!(Foo, Bar)(Foo(1)); foobar.a = 5; assert(foobar.a == 5);
If all types have a matching method, all compatible overloads are exposed:
struct Foo { int fun(int i) { return i; } int fun(int a, int b) { return a + b; } } struct Bar { int fun(int i) { return i; } int fun(int a, int b) { return a + b; } int fun(int a, int b, int c) { return a + b + c; } } auto foobar = SuperStruct!(Foo, Bar)(Foo()); assert(foobar.fun(1) == 1); assert(foobar.fun(1, 2) == 3); assert(!__traits(compiles, foobar.fun(1,2,3))); // no such overload on Foo
If a name is a field on one type and a method on another, it is exposed:
struct Foo { int a; } struct Bar { private int _a; int a() { return _a; } int a(int val) { return _a = val; } } auto foo = SuperStruct!(Foo, Bar)(Foo()); foo.a = 5; // sets Foo.a assert(foo.a == 5); // gets Foo.a auto bar = SuperStruct!(Foo, Bar)(Bar()); bar.a = 5; // invokes Bar.a(int val) assert(bar.a == 5); // invokes Bar.a()
Templated members can be forwarded too:
struct Foo { int val; auto transmorgrify(alias fn1, alias fn2)() { return fn2(fn2(val)); } } struct Bar { auto transmorgrify(alias fn1, alias fn2)() { return 0; } } static auto add1 = (int a) => a + 1; alias FooBar = SuperStruct!(Foo, Bar); FooBar f = Foo(3); assert(f.transmorgrify!(add1, add1) == 5); // 3 + 1 + 1 FooBar b = Bar(); assert(b.transmorgrify!(add1, add1) == 0);
Operators get forwarded to the underlying type
struct Foo { auto opSlice() { return [1,2,3]; } } struct Bar { auto opSlice() { return [4,5,6]; } } SuperStruct!(Foo, Bar) fb = Foo(); assert(fb[] == [1,2,3]);
SuperStructs of the same type can be compared:
struct A { int i; } struct B { int i; } struct C { int i; bool opEquals(T)(T other) { return other.i == i; } } SuperStruct!(A, B, C) a0 = A(0); SuperStruct!(A, B, C) a1 = A(1); SuperStruct!(A, B, C) b0 = B(0); SuperStruct!(A, B, C) c0 = C(0); assert(a0 == a0); // same type, same value assert(a0 != a1); // same type, different value assert(a0 != b0); // incomparable types return false assert(a0 == c0); // different but comparable types // SuperStructs with different sets of source types are not comparable, even // if the types they happen to contain at the moment are. SuperStruct!(A, B) different = A(0); static assert(!__traits(compiles, different == a0));
If members have common signatures but no common return type, the exposed member returns a SuperStruct of the possible return types.
struct A { auto fun() { return 1; } } struct B { auto fun() { return "hi"; } } SuperStruct!(A, B) a = A(); SuperStruct!(A, B) b = B(); assert(a.fun == SuperStruct!(int, string)(1)); assert(b.fun == SuperStruct!(int, string)("hi"));
A Variant which exposes members that are common across all SubTypes.
A SuperStruct!(SubTypes...) wraps an Algebraic!(SubTypes...). It can hold a single value from any of its SubTypes.
Unlike a Variant/Algebraic, SuperStruct exposes access to 'common' members that have compatible signatures.
A member is 'common' if its name describes a public function or field on every one of SubTypes. A call signature for a given member is 'compatible' if, for an instance of any one of SubTypes, that member can be called with the provided set of arguments _and_ all such calls have a common return type.
SuperStruct ignores members beginning with "__" (double underscore).