Refactor the TaggedUnion API.
- Add constructors and assignment operators for unique types - Rename field getters to fooValue and value!() to enable sane property syntax and to avoid confusion when modifying the returned reference - Fix misnomer "isUnionType" instead of "isUnitType"
This commit is contained in:
parent
d7639cdd54
commit
7ede6bf033
22
README.md
22
README.md
|
@ -37,7 +37,7 @@ taggedAny = taggedFoo;
|
|||
// Default initializes to the first field
|
||||
TUnion taggedDef;
|
||||
assert(taggedDef.isCount);
|
||||
assert(taggedDef.getCount == int.init);
|
||||
assert(taggedDef.countValue == int.init);
|
||||
|
||||
// Check type: TUnion.Kind is an enum
|
||||
assert(taggedInt.kind == TUnion.Kind.count);
|
||||
|
@ -53,24 +53,30 @@ assert(taggedFoo.isFoo);
|
|||
assert(taggedAny.isFoo);
|
||||
|
||||
// Set to a different type
|
||||
taggedAny.setStr("bar");
|
||||
taggedAny.strValue("bar");
|
||||
assert(taggedAny.isStr);
|
||||
assert(taggedAny.getStr() == "bar");
|
||||
assert(taggedAny.strValue == "bar");
|
||||
|
||||
// Modify contained value by reference
|
||||
taggedAny.getStr() = "baz";
|
||||
assert(taggedAny.getStr() == "baz");
|
||||
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.get!(TUnion.Kind.count) == 5);
|
||||
assert(taggedInt.get!int == 5);
|
||||
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);
|
||||
assert(!taggedAny.isCount);
|
||||
|
||||
// Unique types can also be set directly
|
||||
taggedAny = "foo";
|
||||
assert(taggedAny.isStr);
|
||||
taggedAny = TUnion(Foo.init);
|
||||
assert(taggedAny.isFoo);
|
||||
```
|
||||
|
||||
|
||||
|
|
|
@ -652,12 +652,12 @@ unittest {
|
|||
*/
|
||||
ref inout(T) get(T, U)(ref inout(TaggedAlgebraic!U) ta)
|
||||
{
|
||||
return ta.m_union.get!T;
|
||||
return ta.m_union.value!T;
|
||||
}
|
||||
/// ditto
|
||||
inout(T) get(T, U)(inout(TaggedAlgebraic!U) ta)
|
||||
{
|
||||
return ta.m_union.get!T;
|
||||
return ta.m_union.value!T;
|
||||
}
|
||||
|
||||
@nogc @safe nothrow unittest {
|
||||
|
@ -1035,7 +1035,7 @@ private string generateConstructors(U)()
|
|||
ret ~= q{
|
||||
this(UnionType.FieldTypeByName!"%1$s" value)
|
||||
{
|
||||
static if (isUnionType!(UnionType.FieldTypeByName!"%1$s"))
|
||||
static if (isUnitType!(UnionType.FieldTypeByName!"%1$s"))
|
||||
m_union.set!(Kind.%1$s)();
|
||||
else
|
||||
m_union.set!(Kind.%1$s)(value);
|
||||
|
@ -1043,7 +1043,7 @@ private string generateConstructors(U)()
|
|||
|
||||
void opAssign(UnionType.FieldTypeByName!"%1$s" value)
|
||||
{
|
||||
static if (isUnionType!(UnionType.FieldTypeByName!"%1$s"))
|
||||
static if (isUnitType!(UnionType.FieldTypeByName!"%1$s"))
|
||||
m_union.set!(Kind.%1$s)();
|
||||
else
|
||||
m_union.set!(Kind.%1$s)(value);
|
||||
|
@ -1060,7 +1060,7 @@ private string generateConstructors(U)()
|
|||
foreach (i, n; TaggedUnion!U.fieldNames) {
|
||||
static if (is(UnionType.FieldTypeByName!"%1$s" == UnionType.FieldTypes[i])) {
|
||||
case __traits(getMember, Kind, n):
|
||||
static if (isUnionType!(UnionType.FieldTypes[i]))
|
||||
static if (isUnitType!(UnionType.FieldTypes[i]))
|
||||
m_union.set!(__traits(getMember, Kind, n))();
|
||||
else m_union.set!(__traits(getMember, Kind, n))(value);
|
||||
return;
|
||||
|
|
|
@ -49,7 +49,7 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
package alias FieldTypeByName(string name) = FieldTypes[__traits(getMember, Kind, name)];
|
||||
|
||||
private {
|
||||
static if (isUnionType!(FieldTypes[0]) || __VERSION__ < 2072) {
|
||||
static if (isUnitType!(FieldTypes[0]) || __VERSION__ < 2072) {
|
||||
void[Largest!FieldTypes.sizeof] m_data;
|
||||
} else {
|
||||
union Dummy {
|
||||
|
@ -72,8 +72,21 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
rawSwap(this, other);
|
||||
}
|
||||
|
||||
static foreach (ti; UniqueTypes!FieldTypes)
|
||||
static if (!isUnitType!(FieldTypes[ti])) {
|
||||
this(FieldTypes[ti] value)
|
||||
{
|
||||
set!(cast(Kind)ti)(value);
|
||||
}
|
||||
|
||||
void opAssign(FieldTypes[ti] value)
|
||||
{
|
||||
set!(cast(Kind)ti)(value);
|
||||
}
|
||||
}
|
||||
|
||||
// disable default construction if first type is not a null/Void type
|
||||
static if (!isUnionType!(FieldTypes[0]) && __VERSION__ < 2072) {
|
||||
static if (!isUnitType!(FieldTypes[0]) && __VERSION__ < 2072) {
|
||||
@disable this();
|
||||
}
|
||||
|
||||
|
@ -180,8 +193,8 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
mixin("alias set"~pascalCase(name)~" = set!(Kind."~name~");");
|
||||
mixin("@property bool is"~pascalCase(name)~"() const { return m_kind == Kind."~name~"; }");
|
||||
|
||||
static if (!isUnionType!(FieldTypes[i])) {
|
||||
mixin("alias get"~pascalCase(name)~" = get!(Kind."~name~");");
|
||||
static if (!isUnitType!(FieldTypes[i])) {
|
||||
mixin("alias "~name~"Value = value!(Kind."~name~");");
|
||||
|
||||
mixin("static TaggedUnion "~name~"(FieldTypes["~i.stringof~"] value)"
|
||||
~ "{ TaggedUnion tu; tu.set!(Kind."~name~")(move(value)); return tu; }");
|
||||
|
@ -193,7 +206,15 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
|
||||
}
|
||||
|
||||
ref inout(FieldTypes[kind]) get(Kind kind)()
|
||||
/** Accesses the contained value by reference.
|
||||
|
||||
The specified `kind` must equal the current value of the `this.kind`
|
||||
property. Setting a different type must be done with `set` or `opAssign`
|
||||
instead.
|
||||
|
||||
See_Also: `set`, `opAssign`
|
||||
*/
|
||||
@property ref inout(FieldTypes[kind]) value(Kind kind)()
|
||||
inout {
|
||||
if (this.kind != kind) {
|
||||
enum msg(.string k_is) = "Attempt to get kind "~kind.stringof~" from tagged union with kind "~k_is;
|
||||
|
@ -208,7 +229,14 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
}
|
||||
|
||||
|
||||
ref inout(T) get(T)() inout
|
||||
/** Accesses the contained value by reference.
|
||||
|
||||
The specified type `T` must equal the type of the currently set value.
|
||||
Setting a different type must be done with `set` or `opAssign` instead.
|
||||
|
||||
See_Also: `set`, `opAssign`
|
||||
*/
|
||||
@property ref inout(T) value(T)() inout
|
||||
if (staticIndexOf!(T, FieldTypes) >= 0)
|
||||
{
|
||||
final switch (this.kind) {
|
||||
|
@ -223,8 +251,10 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
}
|
||||
}
|
||||
|
||||
/** Sets a new value of the specified `kind`.
|
||||
*/
|
||||
ref FieldTypes[kind] set(Kind kind)(FieldTypes[kind] value)
|
||||
if (!isUnionType!(FieldTypes[kind]))
|
||||
if (!isUnitType!(FieldTypes[kind]))
|
||||
{
|
||||
if (m_kind != kind) {
|
||||
destroy(this);
|
||||
|
@ -237,8 +267,10 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
return trustedGet!(FieldTypes[kind]);
|
||||
}
|
||||
|
||||
/** Sets a `void` value of the specified kind.
|
||||
*/
|
||||
void set(Kind kind)()
|
||||
if (isUnionType!(FieldTypes[kind]))
|
||||
if (isUnitType!(FieldTypes[kind]))
|
||||
{
|
||||
if (m_kind != kind) {
|
||||
destroy(this);
|
||||
|
@ -262,15 +294,15 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
assert(tu.kind == TU.Kind.count);
|
||||
assert(tu.isCount); // qequivalent to the line above
|
||||
assert(!tu.isText);
|
||||
assert(tu.get!(TU.Kind.count) == int.init);
|
||||
assert(tu.value!(TU.Kind.count) == int.init);
|
||||
|
||||
// set to a specific count
|
||||
tu.setCount(42);
|
||||
assert(tu.isCount);
|
||||
assert(tu.getCount() == 42);
|
||||
assert(tu.get!(TU.Kind.count) == 42);
|
||||
assert(tu.get!int == 42); // can also get by type
|
||||
assert(tu.getCount() == 42);
|
||||
assert(tu.countValue == 42);
|
||||
assert(tu.value!(TU.Kind.count) == 42);
|
||||
assert(tu.value!int == 42); // can also get by type
|
||||
assert(tu.countValue == 42);
|
||||
|
||||
// assign a new tagged algebraic value
|
||||
tu = TU.count(43);
|
||||
|
@ -281,15 +313,21 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
assert(tu != TU.text("hello"));
|
||||
|
||||
// modify by reference
|
||||
tu.getCount()++;
|
||||
assert(tu.getCount() == 44);
|
||||
tu.countValue++;
|
||||
assert(tu.countValue == 44);
|
||||
|
||||
// set the second field
|
||||
tu.setText("hello");
|
||||
assert(!tu.isCount);
|
||||
assert(tu.isText);
|
||||
assert(tu.kind == TU.Kind.text);
|
||||
assert(tu.getText() == "hello");
|
||||
assert(tu.textValue == "hello");
|
||||
|
||||
// unique types can also be directly constructed
|
||||
tu = TU(12);
|
||||
assert(tu.countValue == 12);
|
||||
tu = TU("foo");
|
||||
assert(tu.textValue == "foo");
|
||||
}
|
||||
|
||||
///
|
||||
|
@ -313,7 +351,7 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum))
|
|||
|
||||
tu.setText("foo");
|
||||
assert(tu.kind == E.text);
|
||||
assert(tu.getText == "foo");
|
||||
assert(tu.textValue == "foo");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -323,7 +361,7 @@ unittest { // test for name clashes
|
|||
TU tu;
|
||||
tu = TU.string("foo");
|
||||
assert(tu.isString);
|
||||
assert(tu.getString() == "foo");
|
||||
assert(tu.stringValue == "foo");
|
||||
}
|
||||
|
||||
|
||||
|
@ -348,14 +386,14 @@ template visit(VISITORS...)
|
|||
final switch (tu.kind) {
|
||||
static foreach (k; EnumMembers!(TU.Kind)) {
|
||||
case k: {
|
||||
static if (isUnionType!(TU.FieldTypes[k]))
|
||||
static if (isUnitType!(TU.FieldTypes[k]))
|
||||
alias T = void;
|
||||
else alias T = TU.FieldTypes[k];
|
||||
alias h = selectHandler!(T, VISITORS);
|
||||
static if (is(typeof(h) == typeof(null))) static assert(false, "No visitor defined for type type "~T.stringof);
|
||||
else static if (is(typeof(h) == string)) static assert(false, h);
|
||||
else static if (is(T == void)) return h();
|
||||
else return h(tu.get!k);
|
||||
else return h(tu.value!k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -433,14 +471,14 @@ template tryVisit(VISITORS...)
|
|||
final switch (tu.kind) {
|
||||
static foreach (k; EnumMembers!(TU.Kind)) {
|
||||
case k: {
|
||||
static if (isUnionType!(TU.FieldTypes[k]))
|
||||
static if (isUnitType!(TU.FieldTypes[k]))
|
||||
alias T = void;
|
||||
else alias T = TU.FieldTypes[k];
|
||||
alias h = selectHandler!(T, VISITORS);
|
||||
static if (is(typeof(h) == typeof(null))) throw new Exception("Type "~T.stringof~" not handled by any visitor.");
|
||||
else static if (is(typeof(h) == string)) static assert(false, h);
|
||||
else static if (is(T == void)) return h();
|
||||
else return h(tu.get!k);
|
||||
else return h(tu.value!k);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -462,7 +500,7 @@ unittest {
|
|||
assertThrown(tu.tryVisit!((string s) { assert(false); }));
|
||||
}
|
||||
|
||||
enum isUnionType(T) = is(T == Void) || is(T == void) || is(T == typeof(null));
|
||||
enum isUnitType(T) = is(T == Void) || is(T == void) || is(T == typeof(null));
|
||||
|
||||
private template validateHandlers(TU, VISITORS...)
|
||||
{
|
||||
|
@ -594,6 +632,29 @@ deprecated alias TypeEnum(U) = UnionFieldEnum!U;
|
|||
private alias UnionKindTypes(FieldEnum) = staticMap!(TypeOf, EnumMembers!FieldEnum);
|
||||
private alias UnionKindNames(FieldEnum) = AliasSeq!(__traits(allMembers, FieldEnum));
|
||||
|
||||
private template UniqueTypes(Types...) {
|
||||
template impl(size_t i) {
|
||||
static if (i < Types.length) {
|
||||
alias T = Types[i];
|
||||
static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i+1 .. $]) < 0)
|
||||
alias impl = AliasSeq!(i, impl!(i+1));
|
||||
else alias impl = AliasSeq!(impl!(i+1));
|
||||
} else alias impl = AliasSeq!();
|
||||
}
|
||||
alias UniqueTypes = impl!0;
|
||||
}
|
||||
|
||||
private template AmbiguousTypes(Types...) {
|
||||
template impl(size_t i) {
|
||||
static if (i < Types.length) {
|
||||
alias T = Types[i];
|
||||
static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i+1 .. $]) >= 0)
|
||||
alias impl = AliasSeq!(i, impl!(i+1));
|
||||
else alias impl = impl!(i+1);
|
||||
} else alias impl = AliasSeq!();
|
||||
}
|
||||
alias AmbiguousTypeFields = impl!0;
|
||||
}
|
||||
|
||||
|
||||
package void rawEmplace(T)(void[] dst, ref T src)
|
||||
|
|
Loading…
Reference in a new issue