Find a file
Sönke Ludwig f8a9b8c651 Add stronger restrictions to TA.opDispatch. Fixes #9.
Defines opDispatch as two levels of nested templates, so that the outer level only checks the member name to match any member defined by one of the contained types. This allows hasMember!TA  to report proper values instead of always assuming `true`.

As a side-effect, `isOutputRange!(TA, T)` now only returns true, if any of the contained types is actually a matching output range.
2019-10-11 11:13:57 +02:00
source/taggedalgebraic Add stronger restrictions to TA.opDispatch. Fixes #9. 2019-10-11 11:13:57 +02:00
.codecov.yml Switch to codecov.io. 2018-01-18 00:02:06 +01:00
.editorconfig Add .travis.yml and .editorconfig. See #2. 2016-01-27 23:02:37 +01:00
.gitignore Use a more modern .gitignore 2019-06-20 16:33:27 +09:00
.travis.yml Test up to DMD 2.088.0 and LDC 1.17.0. 2019-10-11 10:35:08 +02:00
dub.sdl Convert package recipe to SDLang. 2016-07-04 11:18:58 +02:00
README.md Test up to DMD 2.088.0 and LDC 1.17.0. 2019-10-11 10:35:08 +02:00
travis.sh Ddox fix and CI test added 2019-03-05 19:04:45 +07:00

TaggedAlgebraic

Implementation of a generic TaggedUnion type along with a TaggedAlgebraic type that forwards all methods and operators of the contained types using dynamic dispatch.

Build Status codecov

Usage of TaggedUnion

import taggedalgebraic;

struct Foo {
	string name;
	void bar() {}
}

union Base {
	int count;
	int offset;
	string str;
	Foo foo;
}

alias TUnion = TaggedUnion!Base;

// Instantiate
TUnion taggedInt = TUnion.count(5);
TUnion taggedString = TUnion.str("Hello");
TUnion taggedFoo = TUnion.foo;
TUnion taggedAny = taggedInt;
taggedAny = taggedString;
taggedAny = taggedFoo;

// Default initializes to the first field
TUnion taggedDef;
assert(taggedDef.isCount);
assert(taggedDef.countValue == int.init);

// Check type: TUnion.Kind is an enum
assert(taggedInt.kind == TUnion.Kind.count);
assert(taggedString.kind == TUnion.Kind.str);
assert(taggedFoo.kind == TUnion.Kind.foo);
assert(taggedAny.kind == TUnion.Kind.foo);

// A shorter syntax is also available
assert(taggedInt.isCount);
assert(!taggedInt.isOffset);
assert(taggedString.isStr);
assert(taggedFoo.isFoo);
assert(taggedAny.isFoo);

// Set to a different type
taggedAny.setStr("bar");
assert(taggedAny.isStr);
assert(taggedAny.strValue == "bar");

// Modify contained value by reference
taggedAny.strValue = "baz";
assert(taggedAny.strValue == "baz");

// In addition to the getter, the contained value can be extracted using get!()
// or by casting
assert(taggedInt.value!(TUnion.Kind.count) == 5);
assert(taggedInt.value!int == 5);
assert(cast(byte)taggedInt == 5);

// Multiple kinds of the same type are supported
taggedAny.setOffset(5);
assert(taggedAny.isOffset);
assert(!taggedAny.isCount);

// Unique types can also be set directly
taggedAny = "foo";
assert(taggedAny.isStr);
taggedAny = TUnion(Foo.init);
assert(taggedAny.isFoo);

Usage of TaggedAlgebraic

import taggedalgebraic;

struct Foo {
	string name;
	void bar() {}
}

union Base {
	int i;
	string str;
	Foo foo;
}

alias TAlgebraic = TaggedAlgebraic!Base;

// Instantiate
TAlgebraic taggedInt = 5;
TAlgebraic taggedString = "Hello";
TAlgebraic taggedFoo = Foo();
TAlgebraic taggedAny = taggedInt;
taggedAny = taggedString;
taggedAny = taggedFoo;

// Check type: TAlgebraic.Kind is an enum
assert(taggedInt.kind == TAlgebraic.Kind.i);
assert(taggedString.kind == TAlgebraic.Kind.str);
assert(taggedFoo.kind == TAlgebraic.Kind.foo);
assert(taggedAny.kind == TAlgebraic.Kind.foo);

// In most cases, can simply use as-is
auto num = 4 + taggedInt;
auto msg = taggedString ~ " World!";
taggedFoo.bar();
if (taggedAny.kind == TAlgebraic.Kind.foo) // Make sure to check type first!
	taggedAny.bar();
//taggedString.bar(); // AssertError: Not a Foo!

// Convert back by casting
auto i   = cast(int)    taggedInt;
auto str = cast(string) taggedString;
auto foo = cast(Foo)    taggedFoo;
if (taggedAny.kind == TAlgebraic.Kind.foo) // Make sure to check type first!
	auto foo2 = cast(Foo) taggedAny;
//cast(Foo) taggedString; // AssertError!

// Kind is an enum, so final switch is supported:
final switch (taggedAny.kind) {
	case TAlgebraic.Kind.i:
		// It's "int i"
		break;

	case TAlgebraic.Kind.str:
		// It's "string str"
		break;

	case TAlgebraic.Kind.foo:
		// It's "Foo foo"
		break;
}

Compiler support

The library is tested to work on the following compilers:

  • DMD 2.076.1 up to 2.088.0
  • LDC 1.6.0 up to 1.17.0