typecons.Variant: Make public. Fix #73
This commit is contained in:
parent
5453f6417f
commit
c1535e8752
@ -28,6 +28,11 @@ matrix:
|
|||||||
d: dmd-$LATEST
|
d: dmd-$LATEST
|
||||||
env: DDOC=true
|
env: DDOC=true
|
||||||
os: linux
|
os: linux
|
||||||
|
allow_failures:
|
||||||
|
- name: D-Scanner
|
||||||
|
d: dmd-$LATEST
|
||||||
|
env: DSCANNER=0.5.11
|
||||||
|
os: linux
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
apt:
|
apt:
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
module tanya.typecons;
|
module tanya.typecons;
|
||||||
|
|
||||||
import tanya.algorithm.mutation;
|
import tanya.algorithm.mutation;
|
||||||
|
import tanya.conv;
|
||||||
import tanya.format;
|
import tanya.format;
|
||||||
import tanya.functional;
|
import tanya.functional;
|
||||||
import tanya.meta.metafunction;
|
import tanya.meta.metafunction;
|
||||||
@ -562,20 +563,18 @@ Option!T option(T)()
|
|||||||
assert(option(5) == 5);
|
assert(option(5) == 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct VariantAccessorInfo
|
|
||||||
{
|
|
||||||
string accessor;
|
|
||||||
size_t tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tagged union.
|
* 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:
|
* Params:
|
||||||
* Specs = Types of the union members.
|
* Specs = Types this $(D_SPYBMOL Variant) can hold.
|
||||||
*/
|
*/
|
||||||
package (tanya) template Variant(Specs...)
|
template Variant(Specs...)
|
||||||
if (isTypeTuple!Specs)
|
if (isTypeTuple!Specs && NoDuplicates!Specs.length == Specs.length)
|
||||||
{
|
{
|
||||||
union AlignedUnion(Args...)
|
union AlignedUnion(Args...)
|
||||||
{
|
{
|
||||||
@ -589,6 +588,12 @@ if (isTypeTuple!Specs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private struct VariantAccessorInfo
|
||||||
|
{
|
||||||
|
string accessor;
|
||||||
|
ptrdiff_t tag;
|
||||||
|
}
|
||||||
|
|
||||||
template accessor(T, Union)
|
template accessor(T, Union)
|
||||||
{
|
{
|
||||||
enum VariantAccessorInfo info = accessorImpl!(T, Union, 1);
|
enum VariantAccessorInfo info = accessorImpl!(T, Union, 1);
|
||||||
@ -616,23 +621,84 @@ if (isTypeTuple!Specs)
|
|||||||
private ptrdiff_t tag = -1;
|
private ptrdiff_t tag = -1;
|
||||||
private AlignedUnion!Types values;
|
private AlignedUnion!Types values;
|
||||||
|
|
||||||
this(T)(auto ref T value)
|
/**
|
||||||
|
* 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))
|
if (canFind!(T, Types))
|
||||||
{
|
{
|
||||||
opAssign!T(forward!value);
|
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) was initialized.
|
||||||
|
*
|
||||||
|
* Returns: $(D_KEYWORD true) if this $(D_PSYMBOL Variant) contains a
|
||||||
|
* value, $(D_KEYWORD false) otherwise.
|
||||||
|
*/
|
||||||
bool hasValue() const
|
bool hasValue() const
|
||||||
{
|
{
|
||||||
return this.tag != -1;
|
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
|
bool peek(T)() const
|
||||||
if (canFind!(T, Types))
|
if (canFind!(T, Types))
|
||||||
{
|
{
|
||||||
return this.tag == staticIndexOf!(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
|
ref inout(T) get(T)() inout
|
||||||
if (canFind!(T, Types))
|
if (canFind!(T, Types))
|
||||||
in (this.tag == staticIndexOf!(T, Types), "Variant isn't initialized")
|
in (this.tag == staticIndexOf!(T, Types), "Variant isn't initialized")
|
||||||
@ -640,15 +706,71 @@ if (isTypeTuple!Specs)
|
|||||||
mixin("return " ~ accessor!(T, AlignedUnion!Types).accessor ~ ";");
|
mixin("return " ~ accessor!(T, AlignedUnion!Types).accessor ~ ";");
|
||||||
}
|
}
|
||||||
|
|
||||||
typeof(this) opAssign(T)(auto ref T value)
|
/**
|
||||||
|
* 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))
|
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);
|
this.tag = staticIndexOf!(T, Types);
|
||||||
mixin(accessor!(T, AlignedUnion!Types).accessor ~ " = forward!value;");
|
|
||||||
|
enum string accessorMixin = accessor!(T, AlignedUnion!Types).accessor;
|
||||||
|
moveEmplace(that, mixin(accessorMixin));
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
TypeInfo type()
|
private ref typeof(this) copyAssign(T)(ref T that)
|
||||||
|
{
|
||||||
|
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, return
|
||||||
|
* $(D_KEYWORD null).
|
||||||
|
*
|
||||||
|
* Returns: $(D_PSYMBOL TypeInfo) of the current type.
|
||||||
|
*/
|
||||||
|
@property TypeInfo type()
|
||||||
{
|
{
|
||||||
static foreach (i, Type; Types)
|
static foreach (i, Type; Types)
|
||||||
{
|
{
|
||||||
@ -657,9 +779,29 @@ if (isTypeTuple!Specs)
|
|||||||
return typeid(Type);
|
return typeid(Type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(false, "Variant isn't initialized");
|
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
|
bool opEquals()(auto ref inout Variant that) inout
|
||||||
{
|
{
|
||||||
if (this.tag != that.tag)
|
if (this.tag != that.tag)
|
||||||
@ -678,6 +820,18 @@ if (isTypeTuple!Specs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
@nogc nothrow pure @safe unittest
|
@nogc nothrow pure @safe unittest
|
||||||
{
|
{
|
||||||
Variant!(int, double) variant;
|
Variant!(int, double) variant;
|
||||||
@ -748,3 +902,33 @@ if (isTypeTuple!Specs)
|
|||||||
Variant!(int, double) variant1, variant2;
|
Variant!(int, double) variant1, variant2;
|
||||||
assert(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