Find a file
Sönke Ludwig fd85603576 Compute the proper struct alignment for TaggedUnion.
Before this change, the alignment of TaggedAlgebraic was always 4, which in turn had a 50% chance of aligning 8-byte pointers on a non-8-byte boundary, causing the pointer to become invisible to the GC (and less bad, causing performance degradation when dereferencing the pointer). This would happen in particular when the total size of the TaggedUnion/TaggedAlgebraic was not divisible by 8 and an array of values was built.
2019-04-16 11:37:04 +02:00
source/taggedalgebraic Compute the proper struct alignment for TaggedUnion. 2019-04-16 11:37:04 +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 Add .gitignore. 2018-01-18 00:53:16 +01:00
.travis.yml Test on the latest compiler versions and drop support for DMD 2.074.1 and below. 2019-02-22 03:08:55 +01:00
dub.sdl Convert package recipe to SDLang. 2016-07-04 11:18:58 +02:00
README.md Refactor the TaggedUnion API. 2019-02-22 16:11:21 +01: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.strValue("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.084.1
  • LDC 1.6.0 up to 1.14.0