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

this
this(V value)

Construct and populate with an initial value.

Members

Functions

opAssign
auto opAssign(V value)
Undocumented in source. Be warned that the author may not have intended to support it.
opBinary
auto opBinary(T other)

ditto ditto

opBinary
auto opBinary(T other)

Perform a binary operation between two superstructs.

opEquals
bool opEquals(T other)

ditto ditto

opEquals
auto opEquals(typeof(this) other)

Compare one SuperStruct to another of the same type.

opIndex
auto opIndex(T t)

Operators are forwarded to the underlying type.

opOpAssign
auto opOpAssign(T other)

ditto ditto

opOpAssign
auto opOpAssign(T other)

Perform a binary operation between two superstructs.

opSlice
auto opSlice()
auto opSlice(A a, B b)

Operators are forwarded to the underlying type.

opUnary
auto opUnary()

ditto ditto

Examples

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"));

Meta