Replace Variant with SumType
This commit is contained in:
parent
7dd4c44140
commit
5453c646f6
@ -163,7 +163,7 @@ parameter is used)
|
|||||||
|
|
||||||
| DMD | GCC |
|
| DMD | GCC |
|
||||||
|:-------:|:---------:|
|
|:-------:|:---------:|
|
||||||
| 2.098.1 | 11.2 |
|
| 2.100.0 | 12.1 |
|
||||||
|
|
||||||
## Further characteristics
|
## Further characteristics
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ if (F.length == 1)
|
|||||||
*
|
*
|
||||||
* Returns: Accumulated value.
|
* 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 (isBidirectionalRange!R)
|
||||||
{
|
{
|
||||||
if (range.empty)
|
if (range.empty)
|
||||||
|
@ -16,6 +16,7 @@ module tanya.net.ip;
|
|||||||
|
|
||||||
import std.algorithm.comparison;
|
import std.algorithm.comparison;
|
||||||
import std.ascii;
|
import std.ascii;
|
||||||
|
import std.sumtype;
|
||||||
import std.typecons;
|
import std.typecons;
|
||||||
import tanya.algorithm.iteration;
|
import tanya.algorithm.iteration;
|
||||||
import tanya.algorithm.mutation;
|
import tanya.algorithm.mutation;
|
||||||
@ -28,7 +29,6 @@ import tanya.meta.transform;
|
|||||||
import tanya.net.iface;
|
import tanya.net.iface;
|
||||||
import tanya.net.inet;
|
import tanya.net.inet;
|
||||||
import tanya.range;
|
import tanya.range;
|
||||||
import tanya.typecons;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* IPv4 internet address.
|
* IPv4 internet address.
|
||||||
@ -1061,7 +1061,7 @@ if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte))
|
|||||||
*/
|
*/
|
||||||
struct Address
|
struct Address
|
||||||
{
|
{
|
||||||
private Variant!(Address4, Address6) address;
|
private SumType!(Address4, Address6) address;
|
||||||
|
|
||||||
@disable this();
|
@disable this();
|
||||||
|
|
||||||
@ -1095,7 +1095,10 @@ struct Address
|
|||||||
*/
|
*/
|
||||||
bool isV4() const @nogc nothrow pure @safe
|
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
|
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.
|
* Precondition: This is an IPv4 address.
|
||||||
*/
|
*/
|
||||||
ref inout(Address4) toV4() inout @nogc nothrow pure @safe
|
Address4 toV4() inout @nogc nothrow pure @safe
|
||||||
in
|
|
||||||
{
|
{
|
||||||
assert(this.address.peek!Address4);
|
return this.address.match!(
|
||||||
}
|
(Address4 address4) => address4,
|
||||||
do
|
_ => assert(false, "Not an IPv4 address")
|
||||||
{
|
);
|
||||||
return this.address.get!Address4;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -1158,14 +1162,12 @@ struct Address
|
|||||||
*
|
*
|
||||||
* Precondition: This is an IPv6 address.
|
* Precondition: This is an IPv6 address.
|
||||||
*/
|
*/
|
||||||
ref inout(Address6) toV6() inout @nogc nothrow pure @safe
|
Address6 toV6() inout @nogc nothrow pure @safe
|
||||||
in
|
|
||||||
{
|
{
|
||||||
assert(this.address.peek!Address6);
|
return this.address.match!(
|
||||||
}
|
(Address6 address6) => address6,
|
||||||
do
|
_ => assert(false, "Not an IPv6 address")
|
||||||
{
|
);
|
||||||
return this.address.get!Address6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -1185,17 +1187,11 @@ struct Address
|
|||||||
* $(D_PSYMBOL Address6.loopback).
|
* $(D_PSYMBOL Address6.loopback).
|
||||||
*/
|
*/
|
||||||
bool isLoopback() const @nogc nothrow pure @safe
|
bool isLoopback() const @nogc nothrow pure @safe
|
||||||
in
|
|
||||||
{
|
{
|
||||||
assert(this.address.hasValue);
|
return this.address.match!(
|
||||||
}
|
(Address4 address) => address.isLoopback(),
|
||||||
do
|
(Address6 address) => address.isLoopback()
|
||||||
{
|
);
|
||||||
if (this.address.peek!Address4)
|
|
||||||
{
|
|
||||||
return this.address.get!Address4.isLoopback();
|
|
||||||
}
|
|
||||||
return this.address.get!Address6.isLoopback();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -1215,17 +1211,11 @@ struct Address
|
|||||||
* $(D_PSYMBOL Address6.isMulticast).
|
* $(D_PSYMBOL Address6.isMulticast).
|
||||||
*/
|
*/
|
||||||
bool isMulticast() const @nogc nothrow pure @safe
|
bool isMulticast() const @nogc nothrow pure @safe
|
||||||
in
|
|
||||||
{
|
{
|
||||||
assert(this.address.hasValue);
|
return this.address.match!(
|
||||||
}
|
(Address4 address) => address.isMulticast(),
|
||||||
do
|
(Address6 address) => address.isMulticast()
|
||||||
{
|
);
|
||||||
if (this.address.peek!Address4)
|
|
||||||
{
|
|
||||||
return this.address.get!Address4.isMulticast();
|
|
||||||
}
|
|
||||||
return this.address.get!Address6.isMulticast();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -1244,17 +1234,11 @@ struct Address
|
|||||||
* See_Also: $(D_PSYMBOL Address4.isAny), $(D_PSYMBOL Address6.isAny).
|
* See_Also: $(D_PSYMBOL Address4.isAny), $(D_PSYMBOL Address6.isAny).
|
||||||
*/
|
*/
|
||||||
bool isAny() const @nogc nothrow pure @safe
|
bool isAny() const @nogc nothrow pure @safe
|
||||||
in
|
|
||||||
{
|
{
|
||||||
assert(this.address.hasValue);
|
return this.address.match!(
|
||||||
}
|
(Address4 address) => address.isAny(),
|
||||||
do
|
(Address6 address) => address.isAny()
|
||||||
{
|
);
|
||||||
if (this.address.peek!Address4)
|
|
||||||
{
|
|
||||||
return this.address.get!Address4.isAny();
|
|
||||||
}
|
|
||||||
return this.address.get!Address6.isAny();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@ -1277,14 +1261,22 @@ struct Address
|
|||||||
* otherwise.
|
* otherwise.
|
||||||
*/
|
*/
|
||||||
bool opEquals(T)(T that) const
|
bool opEquals(T)(T that) const
|
||||||
if (is(Unqual!T == Address4) || is(Unqual!T == Address6))
|
if (is(Unqual!T == Address4))
|
||||||
{
|
{
|
||||||
alias AddressType = Unqual!T;
|
return this.address.match!(
|
||||||
if (this.address.peek!AddressType)
|
(Address4 address) => address == that,
|
||||||
{
|
(Address6 address) => false
|
||||||
return this.address.get!AddressType == that;
|
);
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
///
|
||||||
|
bool opEquals(T)(T that) const
|
||||||
|
if (is(Unqual!T == Address6))
|
||||||
|
{
|
||||||
|
return this.address.match!(
|
||||||
|
(Address4 address) => false,
|
||||||
|
(Address6 address) => address == that,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -152,276 +152,3 @@ template tuple(Names...)
|
|||||||
assert(t.one == 20);
|
assert(t.one == 20);
|
||||||
assert(t.two == 5);
|
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);
|
|
||||||
}
|
|
||||||
|
@ -21,104 +21,3 @@ import tanya.typecons;
|
|||||||
static assert(!is(Tuple!(int, double, char)));
|
static assert(!is(Tuple!(int, double, char)));
|
||||||
static assert(!is(Tuple!(int, "first", double, "second", char, "third")));
|
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)));
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user