From f4b621963c40468b1c1ae82d5d762af3b6f51a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Fri, 22 Feb 2019 17:34:08 +0100 Subject: [PATCH] Add support for self-referential types using "This" placeholder. --- source/taggedalgebraic/taggedunion.d | 92 +++++++++++++++++++--------- 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/source/taggedalgebraic/taggedunion.d b/source/taggedalgebraic/taggedunion.d index e167373..19c9140 100644 --- a/source/taggedalgebraic/taggedunion.d +++ b/source/taggedalgebraic/taggedunion.d @@ -578,45 +578,81 @@ private string pascalCase(string camel_case) return camel_case[0].toUpper ~ camel_case[1 .. $]; } -static if (__VERSION__ >= 2072) { - /** Maps a kind enumeration value to the corresponding field type. +/** Maps a kind enumeration value to the corresponding field type. - `kind` must be a value of the `TaggedAlgebraic!T.Kind` enumeration. - */ - template TypeOf(alias kind) - if (is(typeof(kind) == enum)) - { - static if (isInstanceOf!(UnionFieldEnum, typeof(kind))) { - import std.traits : FieldTypeTuple, TemplateArgsOf; - alias U = TemplateArgsOf!(typeof(kind)); - alias TypeOf = FieldTypeTuple!U[kind]; - } else { - alias Types = UnionKindTypes!(typeof(kind)); - alias uda = AliasSeq!(__traits(getAttributes, kind)); - static if (uda.length == 0) alias TypeOf = void; - else alias TypeOf = uda[0]; - } + `kind` must be a value of the `TaggedAlgebraic!T.Kind` enumeration. +*/ +template TypeOf(alias kind) + if (is(typeof(kind) == enum)) +{ + import std.traits : FieldTypeTuple, TemplateArgsOf; + import std.typecons : ReplaceType; + + static if (isInstanceOf!(UnionFieldEnum, typeof(kind))) { + alias U = TemplateArgsOf!(typeof(kind)); + alias FT = FieldTypeTuple!U[kind]; + } else { + alias U = typeof(kind); + alias Types = UnionKindTypes!(typeof(kind)); + alias uda = AliasSeq!(__traits(getAttributes, kind)); + static if (uda.length == 0) alias FT = void; + else alias FT = uda[0]; } - /// - unittest { - static struct S { - int a; - string b; - string c; - } - alias TU = TaggedUnion!S; + alias TypeOf = ReplaceType!(This, U, FT); +} - static assert(is(TypeOf!(TU.Kind.a) == int)); - static assert(is(TypeOf!(TU.Kind.b) == string)); - static assert(is(TypeOf!(TU.Kind.c) == string)); +/// +unittest { + static struct S { + int a; + string b; + string c; } + alias TU = TaggedUnion!S; + + static assert(is(TypeOf!(TU.Kind.a) == int)); + static assert(is(TypeOf!(TU.Kind.b) == string)); + static assert(is(TypeOf!(TU.Kind.c) == string)); +} + +unittest { + struct S { + TaggedUnion!This[] test; + } + alias TU = TaggedUnion!S; + + TypeOf!(TU.Kind.test) a; + + static assert(is(TypeOf!(TU.Kind.test) == TaggedUnion!S[])); } /// Convenience type that can be used for union fields that have no value (`void` is not allowed). struct Void {} +/** Special type used as a placeholder for `U` within the definition of `U` to + enable self-referential types. + + Note that this is recognized only if used as the first argument to a + template type. +*/ +struct This { Void nothing; } + +/// +unittest { + union U { + TaggedUnion!This[] list; + int number; + string text; + } + alias Node = TaggedUnion!U; + + auto n = Node([Node(12), Node("foo")]); + assert(n.isList); + assert(n.listValue == [Node(12), Node("foo")]); +} + private template UnionFieldEnum(U) { static if (is(U == enum)) alias UnionFieldEnum = U;