From 5453c646f622b0cdf694d0e64813a39c26a70e0d Mon Sep 17 00:00:00 2001 From: Eugen Wissner Date: Mon, 6 Jun 2022 09:46:45 +0200 Subject: [PATCH] Replace Variant with SumType --- README.md | 2 +- source/tanya/algorithm/iteration.d | 2 +- source/tanya/net/ip.d | 102 +++++------ source/tanya/typecons.d | 273 ----------------------------- tests/tanya/tests/typecons.d | 101 ----------- 5 files changed, 49 insertions(+), 431 deletions(-) diff --git a/README.md b/README.md index 053fce9..d6b1074 100644 --- a/README.md +++ b/README.md @@ -163,7 +163,7 @@ parameter is used) | DMD | GCC | |:-------:|:---------:| -| 2.098.1 | 11.2 | +| 2.100.0 | 12.1 | ## Further characteristics diff --git a/source/tanya/algorithm/iteration.d b/source/tanya/algorithm/iteration.d index 8074fc1..f2019da 100644 --- a/source/tanya/algorithm/iteration.d +++ b/source/tanya/algorithm/iteration.d @@ -226,7 +226,7 @@ if (F.length == 1) * * Returns: Accumulated value. */ - auto foldr(R, T)(scope R range, scope return auto ref T init) + auto foldr(R, T)(scope R range, auto ref T init) if (isBidirectionalRange!R) { if (range.empty) diff --git a/source/tanya/net/ip.d b/source/tanya/net/ip.d index 54a27e3..73e7f84 100644 --- a/source/tanya/net/ip.d +++ b/source/tanya/net/ip.d @@ -16,6 +16,7 @@ module tanya.net.ip; import std.algorithm.comparison; import std.ascii; +import std.sumtype; import std.typecons; import tanya.algorithm.iteration; import tanya.algorithm.mutation; @@ -28,7 +29,6 @@ import tanya.meta.transform; import tanya.net.iface; import tanya.net.inet; import tanya.range; -import tanya.typecons; /** * IPv4 internet address. @@ -1061,7 +1061,7 @@ if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte)) */ struct Address { - private Variant!(Address4, Address6) address; + private SumType!(Address4, Address6) address; @disable this(); @@ -1095,7 +1095,10 @@ struct Address */ bool isV4() const @nogc nothrow pure @safe { - return this.address.peek!Address4; + return this.address.match!( + (Address4 address4) => true, + (Address6 address6) => false + ); } /// @@ -1112,7 +1115,10 @@ struct Address */ bool isV6() const @nogc nothrow pure @safe { - return this.address.peek!Address6; + return this.address.match!( + (Address4 address4) => false, + (Address6 address6) => true + ); } /// @@ -1131,14 +1137,12 @@ struct Address * * Precondition: This is an IPv4 address. */ - ref inout(Address4) toV4() inout @nogc nothrow pure @safe - in + Address4 toV4() inout @nogc nothrow pure @safe { - assert(this.address.peek!Address4); - } - do - { - return this.address.get!Address4; + return this.address.match!( + (Address4 address4) => address4, + _ => assert(false, "Not an IPv4 address") + ); } /// @@ -1158,14 +1162,12 @@ struct Address * * Precondition: This is an IPv6 address. */ - ref inout(Address6) toV6() inout @nogc nothrow pure @safe - in + Address6 toV6() inout @nogc nothrow pure @safe { - assert(this.address.peek!Address6); - } - do - { - return this.address.get!Address6; + return this.address.match!( + (Address6 address6) => address6, + _ => assert(false, "Not an IPv6 address") + ); } /// @@ -1185,17 +1187,11 @@ struct Address * $(D_PSYMBOL Address6.loopback). */ bool isLoopback() const @nogc nothrow pure @safe - in { - assert(this.address.hasValue); - } - do - { - if (this.address.peek!Address4) - { - return this.address.get!Address4.isLoopback(); - } - return this.address.get!Address6.isLoopback(); + return this.address.match!( + (Address4 address) => address.isLoopback(), + (Address6 address) => address.isLoopback() + ); } /// @@ -1215,17 +1211,11 @@ struct Address * $(D_PSYMBOL Address6.isMulticast). */ bool isMulticast() const @nogc nothrow pure @safe - in { - assert(this.address.hasValue); - } - do - { - if (this.address.peek!Address4) - { - return this.address.get!Address4.isMulticast(); - } - return this.address.get!Address6.isMulticast(); + return this.address.match!( + (Address4 address) => address.isMulticast(), + (Address6 address) => address.isMulticast() + ); } /// @@ -1244,17 +1234,11 @@ struct Address * See_Also: $(D_PSYMBOL Address4.isAny), $(D_PSYMBOL Address6.isAny). */ bool isAny() const @nogc nothrow pure @safe - in { - assert(this.address.hasValue); - } - do - { - if (this.address.peek!Address4) - { - return this.address.get!Address4.isAny(); - } - return this.address.get!Address6.isAny(); + return this.address.match!( + (Address4 address) => address.isAny(), + (Address6 address) => address.isAny() + ); } /// @@ -1277,14 +1261,22 @@ struct Address * otherwise. */ bool opEquals(T)(T that) const - if (is(Unqual!T == Address4) || is(Unqual!T == Address6)) + if (is(Unqual!T == Address4)) { - alias AddressType = Unqual!T; - if (this.address.peek!AddressType) - { - return this.address.get!AddressType == that; - } - return false; + return this.address.match!( + (Address4 address) => address == that, + (Address6 address) => false + ); + } + + /// + bool opEquals(T)(T that) const + if (is(Unqual!T == Address6)) + { + return this.address.match!( + (Address4 address) => false, + (Address6 address) => address == that, + ); } /// diff --git a/source/tanya/typecons.d b/source/tanya/typecons.d index 5e570f6..68c629e 100644 --- a/source/tanya/typecons.d +++ b/source/tanya/typecons.d @@ -152,276 +152,3 @@ template tuple(Names...) assert(t.one == 20); assert(t.two == 5); } - -/** - * Type that can hold one of the types listed as its template parameters. - * - * $(D_PSYMBOL Variant) is a type similar to $(D_KEYWORD union), but - * $(D_PSYMBOL Variant) keeps track of the actually used type and throws an - * assertion error when trying to access an invalid type at runtime. - * - * Params: - * Specs = Types this $(D_SPYBMOL Variant) can hold. - */ -template Variant(Specs...) -if (isTypeTuple!Specs && NoDuplicates!Specs.length == Specs.length) -{ - union AlignedUnion(Args...) - { - static if (Args.length > 0) - { - Args[0] value; - } - static if (Args.length > 1) - { - AlignedUnion!(Args[1 .. $]) rest; - } - } - - private struct VariantAccessorInfo - { - string accessor; - ptrdiff_t tag; - } - - template accessor(T, Union) - { - enum VariantAccessorInfo info = accessorImpl!(T, Union, 1); - enum accessor = VariantAccessorInfo("this.values" ~ info.accessor, info.tag); - } - - template accessorImpl(T, Union, size_t tag) - { - static if (is(T == typeof(Union.value))) - { - enum accessorImpl = VariantAccessorInfo(".value", tag); - } - else - { - enum VariantAccessorInfo info = accessorImpl!(T, typeof(Union.rest), tag + 1); - enum accessorImpl = VariantAccessorInfo(".rest" ~ info.accessor, info.tag); - } - } - - struct Variant - { - /// Types can be present in this $(D_PSYMBOL Variant). - alias Types = Specs; - - private ptrdiff_t tag = -1; - private AlignedUnion!Types values; - - /** - * Constructs this $(D_PSYMBOL Variant) with one of the types supported - * in it. - * - * Params: - * T = Type of the initial value. - * value = Initial value. - */ - this(T)(ref T value) - if (canFind!(T, Types)) - { - copyAssign!T(value); - } - - /// ditto - this(T)(T value) - if (canFind!(T, Types)) - { - moveAssign!T(value); - } - - ~this() - { - reset(); - } - - this(this) - { - alias pred(U) = hasElaborateCopyConstructor!(U.Seq[1]); - static foreach (Type; Filter!(pred, Enumerate!Types)) - { - if (this.tag == Type.Seq[0]) - { - get!(Type.Seq[1]).__postblit(); - } - } - } - - /** - * Tells whether this $(D_PSYMBOL Variant) is initialized. - * - * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) contains a - * value, $(D_KEYWORD false) otherwise. - */ - bool hasValue() const - { - return this.tag != -1; - } - - /** - * Tells whether this $(D_PSYMBOL Variant) holds currently a value of - * type $(D_PARAM T). - * - * Params: - * T = Examined type. - * - * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) currently - * contains a value of type $(D_PARAM T), $(D_KEYWORD false) - * otherwise. - */ - bool peek(T)() const - if (canFind!(T, Types)) - { - return this.tag == staticIndexOf!(T, Types); - } - - /** - * Returns the underlying value, assuming it is of the type $(D_PARAM T). - * - * Params: - * T = Type of the value should be returned. - * - * Returns: The underyling value. - * - * Precondition: The $(D_PSYMBOL Variant) has a value. - * - * See_Also: $(D_PSYMBOL peek), $(D_PSYMBOL hasValue). - */ - ref inout(T) get(T)() inout - if (canFind!(T, Types)) - in - { - assert(this.tag == staticIndexOf!(T, Types), "Variant isn't initialized"); - } - do - { - mixin("return " ~ accessor!(T, AlignedUnion!Types).accessor ~ ";"); - } - - /** - * Reassigns the value. - * - * Params: - * T = Type of the new value - * that = New value. - * - * Returns: $(D_KEYWORD this). - */ - ref typeof(this) opAssign(T)(T that) - if (canFind!(T, Types)) - { - reset(); - return moveAssign!T(that); - } - - /// ditto - ref typeof(this) opAssign(T)(ref T that) - if (canFind!(T, Types)) - { - reset(); - return copyAssign!T(that); - } - - private ref typeof(this) moveAssign(T)(ref T that) @trusted - { - this.tag = staticIndexOf!(T, Types); - - enum string accessorMixin = accessor!(T, AlignedUnion!Types).accessor; - moveEmplace(that, mixin(accessorMixin)); - - return this; - } - - private ref typeof(this) copyAssign(T)(ref T that) return - { - this.tag = staticIndexOf!(T, Types); - - enum string accessorMixin = accessor!(T, AlignedUnion!Types).accessor; - emplace!T((() @trusted => (&mixin(accessorMixin))[0 .. 1])(), that); - - return this; - } - - private void reset() - { - alias pred(U) = hasElaborateDestructor!(U.Seq[1]); - static foreach (Type; Filter!(pred, Enumerate!Types)) - { - if (this.tag == Type.Seq[0]) - { - destroy(get!(Type.Seq[1])); - } - } - } - - /** - * Returns $(D_PSYMBOL TypeInfo) corresponding to the current type. - * - * If this $(D_PSYMBOL Variant) isn't initialized, returns - * $(D_KEYWORD null). - * - * Returns: $(D_PSYMBOL TypeInfo) of the current type. - */ - @property TypeInfo type() - { - static foreach (i, Type; Types) - { - if (this.tag == i) - { - return typeid(Type); - } - } - return null; - } - - /** - * Compares this $(D_PSYMBOL Variant) with another one with the same - * specification for equality. - * - * $(UL - * $(LI If both hold values of the same type, these values are - * compared.) - * $(LI If they hold values of different types, then the - * $(D_PSYMBOL Variant)s aren't equal.) - * $(LI If only one of them is initialized but another one not, they - * aren't equal.) - * $(LI If neither of them is initialized, they are equal.) - * ) - * - * Params: - * that = The $(D_PSYMBOL Variant) to compare with. - * - * Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) is equal to - * $(D_PARAM that), $(D_KEYWORD false) otherwise. - */ - bool opEquals()(auto ref inout(Variant) that) inout - { - if (this.tag != that.tag) - { - return false; - } - static foreach (i, Type; Types) - { - if (this.tag == i) - { - return get!Type == that.get!Type; - } - } - return true; - } - } -} - -/// -@nogc nothrow pure @safe unittest -{ - Variant!(int, double) variant = 5; - assert(variant.peek!int); - assert(variant.get!int == 5); - - variant = 5.4; - assert(!variant.peek!int); - assert(variant.get!double == 5.4); -} diff --git a/tests/tanya/tests/typecons.d b/tests/tanya/tests/typecons.d index 4765f77..152370c 100644 --- a/tests/tanya/tests/typecons.d +++ b/tests/tanya/tests/typecons.d @@ -21,104 +21,3 @@ import tanya.typecons; static assert(!is(Tuple!(int, double, char))); static assert(!is(Tuple!(int, "first", double, "second", char, "third"))); } - -@nogc nothrow pure @safe unittest -{ - Variant!(int, double) variant; - variant = 5; - assert(variant.peek!int); -} - -@nogc nothrow pure @safe unittest -{ - Variant!(int, double) variant; - variant = 5.0; - assert(!variant.peek!int); -} - -@nogc nothrow pure @safe unittest -{ - Variant!(int, double) variant = 5; - assert(variant.get!int == 5); -} - -@nogc nothrow pure @safe unittest -{ - static assert(is(Variant!(int, float))); - static assert(is(Variant!int)); -} - -@nogc nothrow pure @safe unittest -{ - static struct WithDestructorAndCopy - { - this(this) @nogc nothrow pure @safe - { - } - - ~this() @nogc nothrow pure @safe - { - } - } - static assert(is(Variant!WithDestructorAndCopy)); -} - -// Equality compares the underlying objects -@nogc nothrow pure @safe unittest -{ - Variant!(int, double) variant1 = 5; - Variant!(int, double) variant2 = 5; - assert(variant1 == variant2); -} - -@nogc nothrow pure @safe unittest -{ - Variant!(int, double) variant1 = 5; - Variant!(int, double) variant2 = 6; - assert(variant1 != variant2); -} - -// Differently typed variants aren't equal -@nogc nothrow pure @safe unittest -{ - Variant!(int, double) variant1 = 5; - Variant!(int, double) variant2 = 5.0; - assert(variant1 != variant2); -} - -// Uninitialized variants are equal -@nogc nothrow pure @safe unittest -{ - Variant!(int, double) variant1, variant2; - assert(variant1 == variant2); -} - -// Calls postblit constructor of the active type -@nogc nothrow pure @safe unittest -{ - static struct S - { - bool called; - - this(this) - { - this.called = true; - } - } - Variant!(int, S) variant1 = S(); - auto variant2 = variant1; - assert(variant2.get!S.called); -} - -// Variant.type is null if the Variant doesn't have a value -@nogc nothrow pure @safe unittest -{ - Variant!(int, uint) variant; - assert(variant.type is null); -} - -// Variant can contain only distinct types -@nogc nothrow pure @safe unittest -{ - static assert(!is(Variant!(int, int))); -}