Struct SuperStruct
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).
Constructors
Name | Description |
---|---|
this
|
Construct and populate with an initial value .
|
Methods
Name | Description |
---|---|
opAssign
|
Populate the SuperStruct with an instance of one of its sub-types.
|
opBinary
|
Operators are forwarded to the underlying type. |
opBinary
|
Perform a binary operation between two superstructs. |
opCall
|
Operators are forwarded to the underlying type. |
opCast
|
Extract the wrapped value of type T from the SuperStruct .
Throws a VariantException if conversion to T is not possible.
|
opCmp
|
Operators are forwarded to the underlying type. |
opDollar
|
Operators are forwarded to the underlying type. |
opEquals
|
Operators are forwarded to the underlying type. |
opEquals
|
Compare one SuperStruct to another of the same type.
|
opIndex
|
Operators are forwarded to the underlying type. |
opOpAssign
|
Operators are forwarded to the underlying type. |
opOpAssign
|
Perform a binary operation between two superstructs. |
opSlice
|
Operators are forwarded to the underlying type. |
opUnary
|
Operators are forwarded to the underlying type. |
Example
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);
Example
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
Example
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()
Example
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);
Example
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]);
Example
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));
Example
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"));
Example
Use cast(T)
to convert a SuperStruct
to a subtype
import std.exception : assertThrown;
struct Square { float size; }
struct Circle { float r; }
SuperStruct!(Square, Circle) sqr = Square(4);
SuperStruct!(Square, Circle) cir = Circle(6);
assert((cast(Square) sqr).size == 4);
assert((cast(Circle) cir).r == 6);
assertThrown!VariantException(cast(Circle) sqr);
assertThrown!VariantException(cast(Square) cir);
Authors
Ryan Roden-Corrent (rcorre)
Copyright
© 2015, Ryan Roden-Corrent
License
MIT