mirror of
https://github.com/dlang/phobos.git
synced 2025-04-26 13:10:35 +03:00
sumtype: add tryGet!T, a throwing version of get!T
This commit is contained in:
parent
756e50dde1
commit
688ecf2089
1 changed files with 178 additions and 0 deletions
178
std/sumtype.d
178
std/sumtype.d
|
@ -2893,6 +2893,184 @@ version (D_BetterC) {} else
|
||||||
assert(ctResult == ElaborateCopy());
|
assert(ctResult == ElaborateCopy());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempt to access a `SumType`'s value.
|
||||||
|
*
|
||||||
|
* If the `SumType` does not contain a value of the specified type, an
|
||||||
|
* exception is thrown.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* T = the type of the value being accessed.
|
||||||
|
*/
|
||||||
|
version (D_Exceptions)
|
||||||
|
template tryGet(T)
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The actual `tryGet` function.
|
||||||
|
*
|
||||||
|
* Params:
|
||||||
|
* self = the `SumType` whose value is being accessed.
|
||||||
|
*
|
||||||
|
* Throws: `MatchException` if the value does not have the expected type.
|
||||||
|
*
|
||||||
|
* Returns: the `SumType`'s value.
|
||||||
|
*/
|
||||||
|
auto ref T tryGet(Self)(auto ref Self self)
|
||||||
|
if (isSumType!Self)
|
||||||
|
{
|
||||||
|
static if (__traits(isRef, self))
|
||||||
|
return self.match!tryGetLvalue;
|
||||||
|
else
|
||||||
|
return self.match!tryGetRvalue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helpers to avoid redundant template instantiations
|
||||||
|
|
||||||
|
ref T tryGetLvalue(Value)(ref Value value)
|
||||||
|
{
|
||||||
|
static if (is(Value == T))
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new MatchException(
|
||||||
|
"Tried to get `" ~ T.stringof ~ "`" ~
|
||||||
|
" but found `" ~ Value.stringof ~ "`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T tryGetRvalue(Value)(ref Value value)
|
||||||
|
{
|
||||||
|
static if (is(Value == T))
|
||||||
|
{
|
||||||
|
import core.lifetime : move;
|
||||||
|
|
||||||
|
// Move if possible; otherwise fall back to copy
|
||||||
|
static if (is(typeof(move(value))))
|
||||||
|
{
|
||||||
|
static if (isCopyable!Value)
|
||||||
|
// Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
|
||||||
|
return __ctfe ? value : move(value);
|
||||||
|
else
|
||||||
|
return move(value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new MatchException(
|
||||||
|
"Tried to get `" ~ T.stringof ~ "`" ~
|
||||||
|
" but found `" ~ Value.stringof ~ "`"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Basic usage
|
||||||
|
version (D_Exceptions)
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
SumType!(string, double) example = "hello";
|
||||||
|
|
||||||
|
assert(example.tryGet!string == "hello");
|
||||||
|
|
||||||
|
double result = double.nan;
|
||||||
|
try
|
||||||
|
result = example.tryGet!double;
|
||||||
|
catch (MatchException e)
|
||||||
|
result = 0;
|
||||||
|
|
||||||
|
// Exception was thrown
|
||||||
|
assert(result == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// With type qualifiers
|
||||||
|
version (D_Exceptions)
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
import std.exception : assertThrown;
|
||||||
|
|
||||||
|
const(SumType!(string, double)) example = "const";
|
||||||
|
|
||||||
|
// Qualifier mismatch; throws exception
|
||||||
|
assertThrown!MatchException(example.tryGet!string);
|
||||||
|
// Qualifier matches; no exception
|
||||||
|
assert(example.tryGet!(const(string)) == "const");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// As a predicate
|
||||||
|
version (D_BetterC) {} else
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
import std.algorithm.iteration : map, sum;
|
||||||
|
import std.functional : pipe;
|
||||||
|
import std.exception : assertThrown;
|
||||||
|
|
||||||
|
alias Example = SumType!(string, double);
|
||||||
|
|
||||||
|
auto arr1 = [Example(0), Example(1), Example(2)];
|
||||||
|
auto arr2 = [Example("foo"), Example("bar"), Example("baz")];
|
||||||
|
|
||||||
|
alias trySum = pipe!(map!(tryGet!double), sum);
|
||||||
|
|
||||||
|
assert(trySum(arr1) == 0 + 1 + 2);
|
||||||
|
assertThrown!MatchException(trySum(arr2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Throws if requested type is impossible
|
||||||
|
version (D_Exceptions)
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
import std.exception : assertThrown;
|
||||||
|
|
||||||
|
SumType!int x;
|
||||||
|
|
||||||
|
assertThrown!MatchException(x.tryGet!string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non-copyable types
|
||||||
|
version (D_Exceptions)
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
static struct NoCopy
|
||||||
|
{
|
||||||
|
@disable this(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
SumType!NoCopy lvalue;
|
||||||
|
auto rvalue() => SumType!NoCopy();
|
||||||
|
|
||||||
|
assert(lvalue.tryGet!NoCopy == NoCopy());
|
||||||
|
assert(rvalue.tryGet!NoCopy == NoCopy());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Immovable rvalues
|
||||||
|
version (D_Exceptions)
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
auto rvalue() => const(SumType!string)("hello");
|
||||||
|
|
||||||
|
assert(rvalue.tryGet!(const(string)) == "hello");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Nontrivial rvalues at compile time
|
||||||
|
version (D_Exceptions)
|
||||||
|
@safe unittest
|
||||||
|
{
|
||||||
|
static struct ElaborateCopy
|
||||||
|
{
|
||||||
|
this(this) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum rvalue = SumType!ElaborateCopy();
|
||||||
|
enum ctResult = rvalue.tryGet!ElaborateCopy;
|
||||||
|
|
||||||
|
assert(ctResult == ElaborateCopy());
|
||||||
|
}
|
||||||
|
|
||||||
private void destroyIfOwner(T)(ref T value)
|
private void destroyIfOwner(T)(ref T value)
|
||||||
{
|
{
|
||||||
static if (hasElaborateDestructor!T)
|
static if (hasElaborateDestructor!T)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue