mirror of
https://github.com/dlang/phobos.git
synced 2025-04-28 22:21:09 +03:00
Merge pull request #1090 from someboddy/add-ml-style-exception-handling
Add ML style exception handling
This commit is contained in:
commit
3fd69b6ebc
1 changed files with 171 additions and 0 deletions
171
std/exception.d
171
std/exception.d
|
@ -1215,3 +1215,174 @@ unittest
|
|||
//process(a.sd); // works
|
||||
//process(structuralCast!StorableDocument(d)); // works
|
||||
}
|
||||
|
||||
/++
|
||||
ML-style functional exception handling. Runs the supplied expression and
|
||||
returns its result. If the expression throws a $(D Throwable), runs the
|
||||
supplied error handler instead and return its result. The error handler's
|
||||
type must be the same as the expression's type.
|
||||
|
||||
Params:
|
||||
E = The type of $(D Throwable)s to catch. Defaults to ${D Exception}
|
||||
T = The return type of the expression and the error handler.
|
||||
expression = The expression to run and return its result.
|
||||
errorHandler = The handler to run if the expression throwed.
|
||||
|
||||
Examples:
|
||||
--------------------
|
||||
//Revert to a default value upon an error:
|
||||
assert("x".to!int().ifThrown(0) == 0);
|
||||
--------------------
|
||||
|
||||
You can also chain multiple calls to ifThrown, each capturing errors from the
|
||||
entire preceding expression.
|
||||
|
||||
Example:
|
||||
--------------------
|
||||
//Chaining multiple calls to ifThrown to attempt multiple things in a row:
|
||||
string s="true";
|
||||
assert(s.to!int().
|
||||
ifThrown(cast(int)s.to!double()).
|
||||
ifThrown(cast(int)s.to!bool())
|
||||
== 1);
|
||||
|
||||
//Respond differently to different types of errors
|
||||
assert(enforce("x".to!int() < 1).to!string()
|
||||
.ifThrown!ConvException("not a number")
|
||||
.ifThrown!Exception("number too small")
|
||||
== "not a number");
|
||||
--------------------
|
||||
|
||||
The expression and the errorHandler must have a common type they can both
|
||||
be implicitly casted to, and that type will be the type of the compound
|
||||
expression.
|
||||
|
||||
Examples:
|
||||
--------------------
|
||||
//null and new Object have a common type(Object).
|
||||
static assert(is(typeof(null.ifThrown(new Object())) == Object));
|
||||
static assert(is(typeof((new Object()).ifThrown(null)) == Object));
|
||||
|
||||
//1 and new Object do not have a common type.
|
||||
static assert(!__traits(compiles, 1.ifThrown(new Object())));
|
||||
static assert(!__traits(compiles, (new Object()).ifThrown(1)));
|
||||
--------------------
|
||||
|
||||
If you need to use the actual thrown expection, you can use a delegate.
|
||||
Example:
|
||||
--------------------
|
||||
//Use a lambda to get the thrown object.
|
||||
assert("%s".format().ifThrown!Exception(e => e.classinfo.name) == "std.format.FormatException");
|
||||
--------------------
|
||||
+/
|
||||
//lazy version
|
||||
CommonType!(T1, T2) ifThrown(E : Throwable = Exception, T1, T2)(lazy scope T1 expression, lazy scope T2 errorHandler)
|
||||
{
|
||||
static assert(!is(typeof(return) == void),
|
||||
"The error handler's return value("~T2.stringof~") does not have a common type with the expression("~T1.stringof~").");
|
||||
try
|
||||
{
|
||||
return expression();
|
||||
}
|
||||
catch(E)
|
||||
{
|
||||
return errorHandler();
|
||||
}
|
||||
}
|
||||
|
||||
///ditto
|
||||
//delegate version
|
||||
CommonType!(T1, T2) ifThrown(E : Throwable, T1, T2)(lazy scope T1 expression, scope T2 delegate(E) errorHandler)
|
||||
{
|
||||
static assert(!is(typeof(return) == void),
|
||||
"The error handler's return value("~T2.stringof~") does not have a common type with the expression("~T1.stringof~").");
|
||||
try
|
||||
{
|
||||
return expression();
|
||||
}
|
||||
catch(E e)
|
||||
{
|
||||
return errorHandler(e);
|
||||
}
|
||||
}
|
||||
|
||||
///ditto
|
||||
//delegate version, general overload to catch any Exception
|
||||
CommonType!(T1, T2) ifThrown(T1, T2)(lazy scope T1 expression, scope T2 delegate(Exception) errorHandler)
|
||||
{
|
||||
static assert(!is(typeof(return) == void),
|
||||
"The error handler's return value("~T2.stringof~") does not have a common type with the expression("~T1.stringof~").");
|
||||
try
|
||||
{
|
||||
return expression();
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
return errorHandler(e);
|
||||
}
|
||||
}
|
||||
|
||||
//Verify Examples
|
||||
unittest
|
||||
{
|
||||
//Revert to a default value upon an error:
|
||||
assert("x".to!int().ifThrown(0) == 0);
|
||||
|
||||
//Chaining multiple calls to ifThrown to attempt multiple things in a row:
|
||||
string s="true";
|
||||
assert(s.to!int().
|
||||
ifThrown(cast(int)s.to!double()).
|
||||
ifThrown(cast(int)s.to!bool())
|
||||
== 1);
|
||||
|
||||
//Respond differently to different types of errors
|
||||
assert(enforce("x".to!int() < 1).to!string()
|
||||
.ifThrown!ConvException("not a number")
|
||||
.ifThrown!Exception("number too small")
|
||||
== "not a number");
|
||||
|
||||
//null and new Object have a common type(Object).
|
||||
static assert(is(typeof(null.ifThrown(new Object())) == Object));
|
||||
static assert(is(typeof((new Object()).ifThrown(null)) == Object));
|
||||
|
||||
//1 and new Object do not have a common type.
|
||||
static assert(!__traits(compiles, 1.ifThrown(new Object())));
|
||||
static assert(!__traits(compiles, (new Object()).ifThrown(1)));
|
||||
|
||||
//Use a lambda to get the thrown object.
|
||||
assert("%s".format().ifThrown(e => e.classinfo.name) == "std.format.FormatException");
|
||||
}
|
||||
|
||||
unittest
|
||||
{
|
||||
//Basic behaviour - all versions.
|
||||
assert("1".to!int().ifThrown(0) == 1);
|
||||
assert("x".to!int().ifThrown(0) == 0);
|
||||
assert("1".to!int().ifThrown!ConvException(0) == 1);
|
||||
assert("x".to!int().ifThrown!ConvException(0) == 0);
|
||||
assert("1".to!int().ifThrown(e=>0) == 1);
|
||||
assert("x".to!int().ifThrown(e=>0) == 0);
|
||||
static if (__traits(compiles, 0.ifThrown!Exception(e => 0))) //This will only work with a fix that was not yet pulled
|
||||
{
|
||||
assert("1".to!int().ifThrown!ConvException(e=>0) == 1);
|
||||
assert("x".to!int().ifThrown!ConvException(e=>0) == 0);
|
||||
}
|
||||
|
||||
//Exceptions other than stated not caught.
|
||||
assert("x".to!int().ifThrown!StringException(0).collectException!ConvException() !is null);
|
||||
static if (__traits(compiles, 0.ifThrown!Exception(e => 0))) //This will only work with a fix that was not yet pulled
|
||||
{
|
||||
assert("x".to!int().ifThrown!StringException(e=>0).collectException!ConvException() !is null);
|
||||
}
|
||||
|
||||
//Default does not include errors.
|
||||
int[] a=[];
|
||||
assert(a[0].ifThrown(0).collectException!RangeError() !is null);
|
||||
assert(a[0].ifThrown(e=>0).collectException!RangeError() !is null);
|
||||
|
||||
//Incompatible types are not accepted.
|
||||
static assert(!__traits(compiles, 1.ifThrown(new Object())));
|
||||
static assert(!__traits(compiles, (new Object()).ifThrown(1)));
|
||||
static assert(!__traits(compiles, 1.ifThrown(e=>new Object())));
|
||||
static assert(!__traits(compiles, (new Object()).ifThrown(e=>1)));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue