mirror of
https://github.com/dlang/phobos.git
synced 2025-04-26 21:22:20 +03:00
1555 lines
45 KiB
D
1555 lines
45 KiB
D
// Written in the D programming language.
|
|
|
|
/++
|
|
This module defines functions related to exceptions and general error
|
|
handling. It also defines functions intended to aid in unit testing.
|
|
|
|
Synopsis of some of std.exception's functions:
|
|
--------------------
|
|
string synopsis()
|
|
{
|
|
FILE* f = enforce(fopen("some/file"));
|
|
// f is not null from here on
|
|
FILE* g = enforceEx!WriteException(fopen("some/other/file", "w"));
|
|
// g is not null from here on
|
|
|
|
Exception e = collectException(write(g, readln(f)));
|
|
if (e)
|
|
{
|
|
... an exception occurred...
|
|
... We have the exception to play around with...
|
|
}
|
|
|
|
string msg = collectExceptionMsg(write(g, readln(f)));
|
|
if (msg)
|
|
{
|
|
... an exception occurred...
|
|
... We have the message from the exception but not the exception...
|
|
}
|
|
|
|
char[] line;
|
|
enforce(readln(f, line));
|
|
return assumeUnique(line);
|
|
}
|
|
--------------------
|
|
|
|
Macros:
|
|
WIKI = Phobos/StdException
|
|
|
|
Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-.
|
|
License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0)
|
|
Authors: $(WEB erdani.org, Andrei Alexandrescu) and Jonathan M Davis
|
|
Source: $(PHOBOSSRC std/_exception.d)
|
|
|
|
+/
|
|
module std.exception;
|
|
|
|
import std.array, std.c.string, std.conv, std.range, std.string, std.traits;
|
|
import core.exception, core.stdc.errno;
|
|
|
|
/++
|
|
Asserts that the given expression does $(I not) throw the given type
|
|
of $(D Throwable). If a $(D Throwable) of the given type is thrown,
|
|
it is caught and does not escape assertNotThrown. Rather, an
|
|
$(D AssertError) is thrown. However, any other $(D Throwable)s will escape.
|
|
|
|
Params:
|
|
T = The $(D Throwable) to test for.
|
|
expression = The expression to test.
|
|
msg = Optional message to output on test failure.
|
|
If msg is empty, and the thrown exception has a
|
|
non-empty msg field, the exception's msg field
|
|
will be output on test failure.
|
|
file = The file where the error occurred.
|
|
Defaults to $(D __FILE__).
|
|
line = The line where the error occurred.
|
|
Defaults to $(D __LINE__).
|
|
|
|
Throws:
|
|
$(D AssertError) if the given $(D Throwable) is thrown.
|
|
+/
|
|
void assertNotThrown(T : Throwable = Exception, E)
|
|
(lazy E expression,
|
|
string msg = null,
|
|
string file = __FILE__,
|
|
size_t line = __LINE__)
|
|
{
|
|
try
|
|
{
|
|
expression();
|
|
}
|
|
catch (T t)
|
|
{
|
|
immutable message = msg.empty ? t.msg : msg;
|
|
immutable tail = message.empty ? "." : ": " ~ message;
|
|
throw new AssertError(format("assertNotThrown failed: %s was thrown%s",
|
|
T.stringof, tail),
|
|
file, line, t);
|
|
}
|
|
}
|
|
///
|
|
unittest
|
|
{
|
|
assertNotThrown!StringException(enforceEx!StringException(true, "Error!"));
|
|
|
|
//Exception is the default.
|
|
assertNotThrown(enforceEx!StringException(true, "Error!"));
|
|
|
|
assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
|
|
enforceEx!StringException(false, "Error!"))) ==
|
|
`assertNotThrown failed: StringException was thrown: Error!`);
|
|
}
|
|
unittest
|
|
{
|
|
assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
|
|
enforceEx!StringException(false, ""), "Error!")) ==
|
|
`assertNotThrown failed: StringException was thrown: Error!`);
|
|
|
|
assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
|
|
enforceEx!StringException(false, ""))) ==
|
|
`assertNotThrown failed: StringException was thrown.`);
|
|
|
|
assert(collectExceptionMsg!AssertError(assertNotThrown!StringException(
|
|
enforceEx!StringException(false, ""), "")) ==
|
|
`assertNotThrown failed: StringException was thrown.`);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
void throwEx(Throwable t) { throw t; }
|
|
void nothrowEx() { }
|
|
|
|
try
|
|
{
|
|
assertNotThrown!Exception(nothrowEx());
|
|
}
|
|
catch (AssertError) assert(0);
|
|
|
|
try
|
|
{
|
|
assertNotThrown!Exception(nothrowEx(), "It's a message");
|
|
}
|
|
catch (AssertError) assert(0);
|
|
|
|
try
|
|
{
|
|
assertNotThrown!AssertError(nothrowEx());
|
|
}
|
|
catch (AssertError) assert(0);
|
|
|
|
try
|
|
{
|
|
assertNotThrown!AssertError(nothrowEx(), "It's a message");
|
|
}
|
|
catch (AssertError) assert(0);
|
|
|
|
{
|
|
bool thrown = false;
|
|
try
|
|
{
|
|
assertNotThrown!Exception(
|
|
throwEx(new Exception("It's an Exception")));
|
|
}
|
|
catch (AssertError) thrown = true;
|
|
assert(thrown);
|
|
}
|
|
|
|
{
|
|
bool thrown = false;
|
|
try
|
|
{
|
|
assertNotThrown!Exception(
|
|
throwEx(new Exception("It's an Exception")), "It's a message");
|
|
}
|
|
catch (AssertError) thrown = true;
|
|
assert(thrown);
|
|
}
|
|
|
|
{
|
|
bool thrown = false;
|
|
try
|
|
{
|
|
assertNotThrown!AssertError(
|
|
throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)));
|
|
}
|
|
catch (AssertError) thrown = true;
|
|
assert(thrown);
|
|
}
|
|
|
|
{
|
|
bool thrown = false;
|
|
try
|
|
{
|
|
assertNotThrown!AssertError(
|
|
throwEx(new AssertError("It's an AssertError", __FILE__, __LINE__)),
|
|
"It's a message");
|
|
}
|
|
catch (AssertError) thrown = true;
|
|
assert(thrown);
|
|
}
|
|
}
|
|
|
|
/++
|
|
Asserts that the given expression throws the given type of $(D Throwable).
|
|
The $(D Throwable) is caught and does not escape assertThrown. However,
|
|
any other $(D Throwable)s $(I will) escape, and if no $(D Throwable)
|
|
of the given type is thrown, then an $(D AssertError) is thrown.
|
|
|
|
Params:
|
|
T = The $(D Throwable) to test for.
|
|
expression = The expression to test.
|
|
msg = Optional message to output on test failure.
|
|
file = The file where the error occurred.
|
|
Defaults to $(D __FILE__).
|
|
line = The line where the error occurred.
|
|
Defaults to $(D __LINE__).
|
|
|
|
Throws:
|
|
$(D AssertError) if the given $(D Throwable) is not thrown.
|
|
+/
|
|
void assertThrown(T : Throwable = Exception, E)
|
|
(lazy E expression,
|
|
string msg = null,
|
|
string file = __FILE__,
|
|
size_t line = __LINE__)
|
|
{
|
|
try
|
|
expression();
|
|
catch (T)
|
|
return;
|
|
|
|
throw new AssertError(format("assertThrown failed: No %s was thrown%s%s",
|
|
T.stringof, msg.empty ? "." : ": ", msg),
|
|
file, line);
|
|
}
|
|
///
|
|
unittest
|
|
{
|
|
assertThrown!StringException(enforceEx!StringException(false, "Error!"));
|
|
|
|
//Exception is the default.
|
|
assertThrown(enforceEx!StringException(false, "Error!"));
|
|
|
|
assert(collectExceptionMsg!AssertError(assertThrown!StringException(
|
|
enforceEx!StringException(true, "Error!"))) ==
|
|
`assertThrown failed: No StringException was thrown.`);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
void throwEx(Throwable t) { throw t; }
|
|
void nothrowEx() { }
|
|
|
|
try
|
|
{
|
|
assertThrown!Exception(throwEx(new Exception("It's an Exception")));
|
|
}
|
|
catch (AssertError) assert(0);
|
|
|
|
try
|
|
{
|
|
assertThrown!Exception(throwEx(new Exception("It's an Exception")),
|
|
"It's a message");
|
|
}
|
|
catch(AssertError) assert(0);
|
|
|
|
try
|
|
{
|
|
assertThrown!AssertError(throwEx(new AssertError("It's an AssertError",
|
|
__FILE__, __LINE__)));
|
|
}
|
|
catch (AssertError) assert(0);
|
|
|
|
try
|
|
{
|
|
assertThrown!AssertError(throwEx(new AssertError("It's an AssertError",
|
|
__FILE__, __LINE__)),
|
|
"It's a message");
|
|
}
|
|
catch (AssertError) assert(0);
|
|
|
|
|
|
{
|
|
bool thrown = false;
|
|
try
|
|
assertThrown!Exception(nothrowEx());
|
|
catch(AssertError)
|
|
thrown = true;
|
|
|
|
assert(thrown);
|
|
}
|
|
|
|
{
|
|
bool thrown = false;
|
|
try
|
|
assertThrown!Exception(nothrowEx(), "It's a message");
|
|
catch(AssertError)
|
|
thrown = true;
|
|
|
|
assert(thrown);
|
|
}
|
|
|
|
{
|
|
bool thrown = false;
|
|
try
|
|
assertThrown!AssertError(nothrowEx());
|
|
catch(AssertError)
|
|
thrown = true;
|
|
|
|
assert(thrown);
|
|
}
|
|
|
|
{
|
|
bool thrown = false;
|
|
try
|
|
assertThrown!AssertError(nothrowEx(), "It's a message");
|
|
catch(AssertError)
|
|
thrown = true;
|
|
|
|
assert(thrown);
|
|
}
|
|
}
|
|
|
|
|
|
/++
|
|
If $(D !!value) is true, $(D value) is returned. Otherwise,
|
|
$(D new Exception(msg)) is thrown.
|
|
|
|
Note:
|
|
$(D enforce) is used to throw exceptions and is therefore intended to
|
|
aid in error handling. It is $(I not) intended for verifying the logic
|
|
of your program. That is what $(D assert) is for. Also, do not use
|
|
$(D enforce) inside of contracts (i.e. inside of $(D in) and $(D out)
|
|
blocks and $(D invariant)s), because they will be compiled out when
|
|
compiling with $(I -release). Use $(D assert) in contracts.
|
|
|
|
Example:
|
|
--------------------
|
|
auto f = enforce(fopen("data.txt"));
|
|
auto line = readln(f);
|
|
enforce(line.length, "Expected a non-empty line.");
|
|
--------------------
|
|
+/
|
|
T enforce(T)(T value, lazy const(char)[] msg = null, string file = __FILE__, size_t line = __LINE__)
|
|
if (is(typeof({ if (!value) {} })))
|
|
{
|
|
if (!value) bailOut(file, line, msg);
|
|
return value;
|
|
}
|
|
|
|
/++
|
|
$(RED Deprecated. If passing the file or line number explicitly, please use
|
|
the overload of enforce which takes them as function arguments. Taking
|
|
them as template arguments causes unnecessary template bloat. This
|
|
overload will be removed in June 2015.)
|
|
+/
|
|
deprecated("Use the overload of enforce that takes file and line as function arguments.")
|
|
T enforce(T, string file, size_t line = __LINE__)
|
|
(T value, lazy const(char)[] msg = null)
|
|
if (is(typeof({ if (!value) {} })))
|
|
{
|
|
if (!value) bailOut(file, line, msg);
|
|
return value;
|
|
}
|
|
|
|
/++
|
|
If $(D !!value) is true, $(D value) is returned. Otherwise, the given
|
|
delegate is called.
|
|
|
|
The whole safety and purity are inferred from $(D Dg)'s safety and purity.
|
|
+/
|
|
T enforce(T, Dg, string file = __FILE__, size_t line = __LINE__)
|
|
(T value, scope Dg dg)
|
|
if (isSomeFunction!Dg && is(typeof( dg() )) &&
|
|
is(typeof({ if (!value) {} })))
|
|
{
|
|
if (!value) dg();
|
|
return value;
|
|
}
|
|
|
|
private void bailOut(string file, size_t line, in char[] msg) @safe pure
|
|
{
|
|
throw new Exception(msg.ptr ? msg.idup : "Enforcement failed", file, line);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assert (enforce(123) == 123);
|
|
|
|
try
|
|
{
|
|
enforce(false, "error");
|
|
assert (false);
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
assert (e.msg == "error");
|
|
assert (e.file == __FILE__);
|
|
assert (e.line == __LINE__-7);
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
// Issue 10510
|
|
extern(C) void cFoo() { }
|
|
enforce(false, &cFoo);
|
|
}
|
|
|
|
// purity and safety inference test
|
|
unittest
|
|
{
|
|
import std.typetuple;
|
|
|
|
foreach (EncloseSafe; TypeTuple!(false, true))
|
|
foreach (EnclosePure; TypeTuple!(false, true))
|
|
{
|
|
foreach (BodySafe; TypeTuple!(false, true))
|
|
foreach (BodyPure; TypeTuple!(false, true))
|
|
{
|
|
enum code =
|
|
"delegate void() " ~
|
|
(EncloseSafe ? "@safe " : "") ~
|
|
(EnclosePure ? "pure " : "") ~
|
|
"{ enforce(true, { " ~
|
|
"int n; " ~
|
|
(BodySafe ? "" : "auto p = &n + 10; " ) ~ // unsafe code
|
|
(BodyPure ? "" : "static int g; g = 10; ") ~ // impure code
|
|
"}); " ~
|
|
"}";
|
|
enum expect =
|
|
(BodySafe || !EncloseSafe) && (!EnclosePure || BodyPure);
|
|
|
|
version(none)
|
|
pragma(msg, "safe = ", EncloseSafe?1:0, "/", BodySafe?1:0, ", ",
|
|
"pure = ", EnclosePure?1:0, "/", BodyPure?1:0, ", ",
|
|
"expect = ", expect?"OK":"NG", ", ",
|
|
"code = ", code);
|
|
|
|
static assert(__traits(compiles, mixin(code)()) == expect);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test for bugzilla 8637
|
|
unittest
|
|
{
|
|
struct S
|
|
{
|
|
static int g;
|
|
~this() {} // impure & unsafe destructor
|
|
bool opCast(T:bool)() {
|
|
int* p = cast(int*)0; // unsafe operation
|
|
int n = g; // impure operation
|
|
return true;
|
|
}
|
|
}
|
|
S s;
|
|
|
|
enforce(s);
|
|
enforce(s, {});
|
|
enforce(s, new Exception(""));
|
|
|
|
errnoEnforce(s);
|
|
|
|
alias E1 = Exception;
|
|
static class E2 : Exception
|
|
{
|
|
this(string fn, size_t ln) { super("", fn, ln); }
|
|
}
|
|
static class E3 : Exception
|
|
{
|
|
this(string msg) { super(msg, __FILE__, __LINE__); }
|
|
}
|
|
enforceEx!E1(s);
|
|
enforceEx!E2(s);
|
|
}
|
|
|
|
deprecated unittest
|
|
{
|
|
struct S
|
|
{
|
|
static int g;
|
|
~this() {} // impure & unsafe destructor
|
|
bool opCast(T:bool)() {
|
|
int* p = cast(int*)0; // unsafe operation
|
|
int n = g; // impure operation
|
|
return true;
|
|
}
|
|
}
|
|
S s;
|
|
|
|
enforce!(S, __FILE__, __LINE__)(s, "");
|
|
}
|
|
|
|
/++
|
|
If $(D !!value) is true, $(D value) is returned. Otherwise, $(D ex) is thrown.
|
|
|
|
Example:
|
|
--------------------
|
|
auto f = enforce(fopen("data.txt"));
|
|
auto line = readln(f);
|
|
enforce(line.length, new IOException); // expect a non-empty line
|
|
--------------------
|
|
+/
|
|
T enforce(T)(T value, lazy Throwable ex)
|
|
{
|
|
if (!value) throw ex();
|
|
return value;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assertNotThrown(enforce(true, new Exception("this should not be thrown")));
|
|
assertThrown(enforce(false, new Exception("this should be thrown")));
|
|
}
|
|
|
|
/++
|
|
If $(D !!value) is true, $(D value) is returned. Otherwise,
|
|
$(D new ErrnoException(msg)) is thrown. $(D ErrnoException) assumes that the
|
|
last operation set $(D errno) to an error code.
|
|
|
|
Example:
|
|
--------------------
|
|
auto f = errnoEnforce(fopen("data.txt"));
|
|
auto line = readln(f);
|
|
enforce(line.length); // expect a non-empty line
|
|
--------------------
|
|
+/
|
|
T errnoEnforce(T, string file = __FILE__, size_t line = __LINE__)
|
|
(T value, lazy string msg = null)
|
|
{
|
|
if (!value) throw new ErrnoException(msg, file, line);
|
|
return value;
|
|
}
|
|
|
|
|
|
/++
|
|
If $(D !!value) is $(D true), $(D value) is returned. Otherwise,
|
|
$(D new E(msg, file, line)) is thrown. Or if $(D E) doesn't take a message
|
|
and can be constructed with $(D new E(file, line)), then
|
|
$(D new E(file, line)) will be thrown.
|
|
|
|
Example:
|
|
--------------------
|
|
auto f = enforceEx!FileMissingException(fopen("data.txt"));
|
|
auto line = readln(f);
|
|
enforceEx!DataCorruptionException(line.length);
|
|
--------------------
|
|
+/
|
|
template enforceEx(E : Throwable)
|
|
if (is(typeof(new E("", __FILE__, __LINE__))))
|
|
{
|
|
/++ Ditto +/
|
|
T enforceEx(T)(T value, lazy string msg = "", string file = __FILE__, size_t line = __LINE__)
|
|
{
|
|
if (!value) throw new E(msg, file, line);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
/++ Ditto +/
|
|
template enforceEx(E : Throwable)
|
|
if (is(typeof(new E(__FILE__, __LINE__))) && !is(typeof(new E("", __FILE__, __LINE__))))
|
|
{
|
|
/++ Ditto +/
|
|
T enforceEx(T)(T value, string file = __FILE__, size_t line = __LINE__)
|
|
{
|
|
if (!value) throw new E(file, line);
|
|
return value;
|
|
}
|
|
}
|
|
|
|
unittest
|
|
{
|
|
assertNotThrown(enforceEx!Exception(true));
|
|
assertNotThrown(enforceEx!Exception(true, "blah"));
|
|
assertNotThrown(enforceEx!OutOfMemoryError(true));
|
|
|
|
{
|
|
auto e = collectException(enforceEx!Exception(false));
|
|
assert(e !is null);
|
|
assert(e.msg.empty);
|
|
assert(e.file == __FILE__);
|
|
assert(e.line == __LINE__ - 4);
|
|
}
|
|
|
|
{
|
|
auto e = collectException(enforceEx!Exception(false, "hello", "file", 42));
|
|
assert(e !is null);
|
|
assert(e.msg == "hello");
|
|
assert(e.file == "file");
|
|
assert(e.line == 42);
|
|
}
|
|
|
|
{
|
|
auto e = collectException!Error(enforceEx!OutOfMemoryError(false));
|
|
assert(e !is null);
|
|
assert(e.msg == "Memory allocation failed");
|
|
assert(e.file == __FILE__);
|
|
assert(e.line == __LINE__ - 4);
|
|
}
|
|
|
|
{
|
|
auto e = collectException!Error(enforceEx!OutOfMemoryError(false, "file", 42));
|
|
assert(e !is null);
|
|
assert(e.msg == "Memory allocation failed");
|
|
assert(e.file == "file");
|
|
assert(e.line == 42);
|
|
}
|
|
|
|
static assert(!is(typeof(enforceEx!int(true))));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
alias enf = enforceEx!Exception;
|
|
assertNotThrown(enf(true));
|
|
assertThrown(enf(false, "blah"));
|
|
}
|
|
|
|
|
|
/++
|
|
Catches and returns the exception thrown from the given expression.
|
|
If no exception is thrown, then null is returned and $(D result) is
|
|
set to the result of the expression.
|
|
|
|
Note that while $(D collectException) $(I can) be used to collect any
|
|
$(D Throwable) and not just $(D Exception)s, it is generally ill-advised to
|
|
catch anything that is neither an $(D Exception) nor a type derived from
|
|
$(D Exception). So, do not use $(D collectException) to collect
|
|
non-$(D Exception)s unless you're sure that that's what you really want to
|
|
do.
|
|
|
|
Params:
|
|
T = The type of exception to catch.
|
|
expression = The expression which may throw an exception.
|
|
result = The result of the expression if no exception is thrown.
|
|
+/
|
|
T collectException(T = Exception, E)(lazy E expression, ref E result)
|
|
{
|
|
try
|
|
{
|
|
result = expression();
|
|
}
|
|
catch (T e)
|
|
{
|
|
return e;
|
|
}
|
|
return null;
|
|
}
|
|
///
|
|
unittest
|
|
{
|
|
int b;
|
|
int foo() { throw new Exception("blah"); }
|
|
assert(collectException(foo(), b));
|
|
|
|
int[] a = new int[3];
|
|
import core.exception : RangeError;
|
|
assert(collectException!RangeError(a[4], b));
|
|
}
|
|
|
|
/++
|
|
Catches and returns the exception thrown from the given expression.
|
|
If no exception is thrown, then null is returned. $(D E) can be
|
|
$(D void).
|
|
|
|
Note that while $(D collectException) $(I can) be used to collect any
|
|
$(D Throwable) and not just $(D Exception)s, it is generally ill-advised to
|
|
catch anything that is neither an $(D Exception) nor a type derived from
|
|
$(D Exception). So, do not use $(D collectException) to collect
|
|
non-$(D Exception)s unless you're sure that that's what you really want to
|
|
do.
|
|
|
|
Params:
|
|
T = The type of exception to catch.
|
|
expression = The expression which may throw an exception.
|
|
+/
|
|
T collectException(T : Throwable = Exception, E)(lazy E expression)
|
|
{
|
|
try
|
|
{
|
|
expression();
|
|
}
|
|
catch (T t)
|
|
{
|
|
return t;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
int foo() { throw new Exception("blah"); }
|
|
assert(collectException(foo()));
|
|
}
|
|
|
|
/++
|
|
Catches the exception thrown from the given expression and returns the
|
|
msg property of that exception. If no exception is thrown, then null is
|
|
returned. $(D E) can be $(D void).
|
|
|
|
If an exception is thrown but it has an empty message, then
|
|
$(D emptyExceptionMsg) is returned.
|
|
|
|
Note that while $(D collectExceptionMsg) $(I can) be used to collect any
|
|
$(D Throwable) and not just $(D Exception)s, it is generally ill-advised to
|
|
catch anything that is neither an $(D Exception) nor a type derived from
|
|
$(D Exception). So, do not use $(D collectExceptionMsg) to collect
|
|
non-$(D Exception)s unless you're sure that that's what you really want to
|
|
do.
|
|
|
|
Params:
|
|
T = The type of exception to catch.
|
|
expression = The expression which may throw an exception.
|
|
+/
|
|
string collectExceptionMsg(T = Exception, E)(lazy E expression)
|
|
{
|
|
try
|
|
{
|
|
expression();
|
|
|
|
return cast(string)null;
|
|
}
|
|
catch(T e)
|
|
return e.msg.empty ? emptyExceptionMsg : e.msg;
|
|
}
|
|
///
|
|
unittest
|
|
{
|
|
void throwFunc() { throw new Exception("My Message."); }
|
|
assert(collectExceptionMsg(throwFunc()) == "My Message.");
|
|
|
|
void nothrowFunc() {}
|
|
assert(collectExceptionMsg(nothrowFunc()) is null);
|
|
|
|
void throwEmptyFunc() { throw new Exception(""); }
|
|
assert(collectExceptionMsg(throwEmptyFunc()) == emptyExceptionMsg);
|
|
}
|
|
|
|
/++
|
|
Value that collectExceptionMsg returns when it catches an exception
|
|
with an empty exception message.
|
|
+/
|
|
enum emptyExceptionMsg = "<Empty Exception Message>";
|
|
|
|
/**
|
|
* Casts a mutable array to an immutable array in an idiomatic
|
|
* manner. Technically, $(D assumeUnique) just inserts a cast,
|
|
* but its name documents assumptions on the part of the
|
|
* caller. $(D assumeUnique(arr)) should only be called when
|
|
* there are no more active mutable aliases to elements of $(D
|
|
* arr). To strengthen this assumption, $(D assumeUnique(arr))
|
|
* also clears $(D arr) before returning. Essentially $(D
|
|
* assumeUnique(arr)) indicates commitment from the caller that there
|
|
* is no more mutable access to any of $(D arr)'s elements
|
|
* (transitively), and that all future accesses will be done through
|
|
* the immutable array returned by $(D assumeUnique).
|
|
*
|
|
* Typically, $(D assumeUnique) is used to return arrays from
|
|
* functions that have allocated and built them.
|
|
*
|
|
* Example:
|
|
*
|
|
* ----
|
|
* string letters()
|
|
* {
|
|
* char[] result = new char['z' - 'a' + 1];
|
|
* foreach (i, ref e; result)
|
|
* {
|
|
* e = cast(char)('a' + i);
|
|
* }
|
|
* return assumeUnique(result);
|
|
* }
|
|
* ----
|
|
*
|
|
* The use in the example above is correct because $(D result)
|
|
* was private to $(D letters) and is inaccessible in writing
|
|
* after the function returns. The following example shows an
|
|
* incorrect use of $(D assumeUnique).
|
|
*
|
|
* Bad:
|
|
*
|
|
* ----
|
|
* private char[] buffer;
|
|
* string letters(char first, char last)
|
|
* {
|
|
* if (first >= last) return null; // fine
|
|
* auto sneaky = buffer;
|
|
* sneaky.length = last - first + 1;
|
|
* foreach (i, ref e; sneaky)
|
|
* {
|
|
* e = cast(char)('a' + i);
|
|
* }
|
|
* return assumeUnique(sneaky); // BAD
|
|
* }
|
|
* ----
|
|
*
|
|
* The example above wreaks havoc on client code because it is
|
|
* modifying arrays that callers considered immutable. To obtain an
|
|
* immutable array from the writable array $(D buffer), replace
|
|
* the last line with:
|
|
* ----
|
|
* return to!(string)(sneaky); // not that sneaky anymore
|
|
* ----
|
|
*
|
|
* The call will duplicate the array appropriately.
|
|
*
|
|
* Note that checking for uniqueness during compilation is
|
|
* possible in certain cases, especially when a function is
|
|
* marked as a pure function. The following example does not
|
|
* need to call assumeUnique because the compiler can infer the
|
|
* uniqueness of the array in the pure function:
|
|
* ----
|
|
* string letters() pure
|
|
* {
|
|
* char[] result = new char['z' - 'a' + 1];
|
|
* foreach (i, ref e; result)
|
|
* {
|
|
* e = cast(char)('a' + i);
|
|
* }
|
|
* return result;
|
|
* }
|
|
* ----
|
|
*
|
|
* For more on infering uniqueness see the $(B unique) and
|
|
* $(B lent) keywords in the
|
|
* $(WEB archjava.fluid.cs.cmu.edu/papers/oopsla02.pdf, ArchJava)
|
|
* language.
|
|
*
|
|
* The downside of using $(D assumeUnique)'s
|
|
* convention-based usage is that at this time there is no
|
|
* formal checking of the correctness of the assumption;
|
|
* on the upside, the idiomatic use of $(D assumeUnique) is
|
|
* simple and rare enough to be tolerable.
|
|
*
|
|
*/
|
|
immutable(T)[] assumeUnique(T)(T[] array) pure nothrow
|
|
{
|
|
return .assumeUnique(array); // call ref version
|
|
}
|
|
/// ditto
|
|
immutable(T)[] assumeUnique(T)(ref T[] array) pure nothrow
|
|
{
|
|
auto result = cast(immutable(T)[]) array;
|
|
array = null;
|
|
return result;
|
|
}
|
|
|
|
unittest
|
|
{
|
|
int[] arr = new int[1];
|
|
auto arr1 = assumeUnique(arr);
|
|
assert(is(typeof(arr1) == immutable(int)[]) && arr == null);
|
|
}
|
|
|
|
immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow
|
|
{
|
|
auto result = cast(immutable(T[U])) array;
|
|
array = null;
|
|
return result;
|
|
}
|
|
|
|
// @@@BUG@@@
|
|
version(none) unittest
|
|
{
|
|
int[string] arr = ["a":1];
|
|
auto arr1 = assumeUnique(arr);
|
|
assert(is(typeof(arr1) == immutable(int[string])) && arr == null);
|
|
}
|
|
|
|
/**
|
|
* Wraps a possibly-throwing expression in a $(D nothrow) wrapper so that it
|
|
* can be called by a $(D nothrow) function.
|
|
*
|
|
* This wrapper function documents commitment on the part of the caller that
|
|
* the appropriate steps have been taken to avoid whatever conditions may
|
|
* trigger an exception during the evaluation of $(D expr). If it turns out
|
|
* that the expression $(I does) throw at runtime, the wrapper will throw an
|
|
* $(D AssertError).
|
|
*
|
|
* (Note that $(D Throwable) objects such as $(D AssertError) that do not
|
|
* subclass $(D Exception) may be thrown even from $(D nothrow) functions,
|
|
* since they are considered to be serious runtime problems that cannot be
|
|
* recovered from.)
|
|
*/
|
|
T assumeWontThrow(T)(lazy T expr,
|
|
string msg = null,
|
|
string file = __FILE__,
|
|
size_t line = __LINE__) nothrow
|
|
{
|
|
try
|
|
{
|
|
return expr;
|
|
}
|
|
catch(Exception e)
|
|
{
|
|
immutable tail = msg.empty ? "." : ": " ~ msg;
|
|
throw new AssertError("assumeWontThrow failed: Expression did throw" ~
|
|
tail, file, line);
|
|
}
|
|
}
|
|
|
|
///
|
|
unittest
|
|
{
|
|
import std.math : sqrt;
|
|
|
|
// This function may throw.
|
|
int squareRoot(int x)
|
|
{
|
|
if (x < 0)
|
|
throw new Exception("Tried to take root of negative number");
|
|
return cast(int)sqrt(cast(double)x);
|
|
}
|
|
|
|
// This function never throws.
|
|
int computeLength(int x, int y) nothrow
|
|
{
|
|
// Since x*x + y*y is always positive, we can safely assume squareRoot
|
|
// won't throw, and use it to implement this nothrow function. If it
|
|
// does throw (e.g., if x*x + y*y overflows a 32-bit value), then the
|
|
// program will terminate.
|
|
return assumeWontThrow(squareRoot(x*x + y*y));
|
|
}
|
|
|
|
assert(computeLength(3, 4) == 5);
|
|
}
|
|
|
|
unittest
|
|
{
|
|
void alwaysThrows()
|
|
{
|
|
throw new Exception("I threw up");
|
|
}
|
|
void bad() nothrow
|
|
{
|
|
assumeWontThrow(alwaysThrows());
|
|
}
|
|
assertThrown!AssertError(bad());
|
|
}
|
|
|
|
/**
|
|
The "pointsTo" functions, $(D doesPointTo) and $(D mayPointTo).
|
|
|
|
Returns $(D true) if $(D source)'s representation embeds a pointer
|
|
that points to $(D target)'s representation or somewhere inside
|
|
it.
|
|
|
|
If $(D source) is or contains a dynamic array, then, then these functions will check
|
|
if there is overlap between the dynamic array and $(D target)'s representation.
|
|
|
|
If $(D source) is a class, then pointsTo will handle it as a pointer.
|
|
|
|
If $(D target) is a pointer, a dynamic array or a class, then these functions will only
|
|
check if $(D source) points to $(D target), $(I not) what $(D target) references.
|
|
|
|
If $(D source) is or contains a union, then there may be either false positives or
|
|
false negatives:
|
|
|
|
$(D doesPointTo) will return $(D true) if it is absolutly certain
|
|
$(D source) points to $(D target). It may produce false negatives, but never
|
|
false positives. This function should be prefered when trying to validate
|
|
input data.
|
|
|
|
$(D mayPointTo) will return $(D false) if it is absolutly certain
|
|
$(D source) does not point to $(D target). It may produce false positives, but never
|
|
false negatives. This function should be prefered for defensively choosing a
|
|
code path.
|
|
|
|
Note: Evaluating $(D pointsTo(x, x)) checks whether $(D x) has
|
|
internal pointers. This should only be done as an assertive test,
|
|
as the language is free to assume objects don't have internal pointers
|
|
(TDPL 7.1.3.5).
|
|
*/
|
|
bool doesPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow
|
|
if (__traits(isRef, source) || isDynamicArray!S ||
|
|
isPointer!S || is(S == class))
|
|
{
|
|
static if (isPointer!S || is(S == class) || is(S == interface))
|
|
{
|
|
const m = cast(void*) source,
|
|
b = cast(void*) &target, e = b + target.sizeof;
|
|
return b <= m && m < e;
|
|
}
|
|
else static if (is(S == struct) || is(S == union))
|
|
{
|
|
foreach (i, Subobj; typeof(source.tupleof))
|
|
static if (!isUnionAliased!(S, i))
|
|
if (doesPointTo(source.tupleof[i], target)) return true;
|
|
return false;
|
|
}
|
|
else static if (isStaticArray!S)
|
|
{
|
|
foreach (size_t i; 0 .. S.length)
|
|
if (doesPointTo(source[i], target)) return true;
|
|
return false;
|
|
}
|
|
else static if (isDynamicArray!S)
|
|
{
|
|
return overlap(cast(void[])source, cast(void[])(&target)[0 .. 1]).length != 0;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool mayPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow
|
|
if (__traits(isRef, source) || isDynamicArray!S ||
|
|
isPointer!S || is(S == class))
|
|
{
|
|
static if (isPointer!S || is(S == class) || is(S == interface))
|
|
{
|
|
const m = cast(void*) source,
|
|
b = cast(void*) &target, e = b + target.sizeof;
|
|
return b <= m && m < e;
|
|
}
|
|
else static if (is(S == struct) || is(S == union))
|
|
{
|
|
foreach (i, Subobj; typeof(source.tupleof))
|
|
if (mayPointTo(source.tupleof[i], target)) return true;
|
|
return false;
|
|
}
|
|
else static if (isStaticArray!S)
|
|
{
|
|
foreach (size_t i; 0 .. S.length)
|
|
if (mayPointTo(source[i], target)) return true;
|
|
return false;
|
|
}
|
|
else static if (isDynamicArray!S)
|
|
{
|
|
return overlap(cast(void[])source, cast(void[])(&target)[0 .. 1]).length != 0;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// for shared objects
|
|
bool doesPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow
|
|
{
|
|
return doesPointTo!(shared S, shared T, void)(source, target);
|
|
}
|
|
bool mayPointTo(S, T)(auto ref const shared S source, ref const shared T target) @trusted pure nothrow
|
|
{
|
|
return mayPointTo!(shared S, shared T, void)(source, target);
|
|
}
|
|
|
|
deprecated ("pointsTo is ambiguous. Please use either of doesPointTo or mayPointTo")
|
|
alias pointsTo = doesPointTo;
|
|
|
|
/+
|
|
Returns true if the field at index $(D i) in ($D T) shares its address with another field.
|
|
|
|
Note: This does not merelly check if the field is a member of an union, but also that
|
|
it is not a single child.
|
|
+/
|
|
package enum isUnionAliased(T, size_t i) = isUnionAliasedImpl!T(T.tupleof[i].offsetof);
|
|
private bool isUnionAliasedImpl(T)(size_t offset)
|
|
{
|
|
int count = 0;
|
|
foreach (i, U; typeof(T.tupleof))
|
|
if (T.tupleof[i].offsetof == offset)
|
|
++count;
|
|
return count >= 2;
|
|
}
|
|
//
|
|
unittest
|
|
{
|
|
static struct S
|
|
{
|
|
int a0; //Not aliased
|
|
union
|
|
{
|
|
int a1; //Not aliased
|
|
}
|
|
union
|
|
{
|
|
int a2; //Aliased
|
|
int a3; //Aliased
|
|
}
|
|
union A4
|
|
{
|
|
int b0; //Not aliased
|
|
};
|
|
A4 a4;
|
|
union A5
|
|
{
|
|
int b0; //Aliased
|
|
int b1; //Aliased
|
|
};
|
|
A5 a5;
|
|
}
|
|
|
|
static assert(!isUnionAliased!(S, 0)); //a0;
|
|
static assert(!isUnionAliased!(S, 1)); //a1;
|
|
static assert( isUnionAliased!(S, 2)); //a2;
|
|
static assert( isUnionAliased!(S, 3)); //a3;
|
|
static assert(!isUnionAliased!(S, 4)); //a4;
|
|
static assert(!isUnionAliased!(S.A4, 0)); //a4.b0;
|
|
static assert(!isUnionAliased!(S, 5)); //a5;
|
|
static assert( isUnionAliased!(S.A5, 0)); //a5.b0;
|
|
static assert( isUnionAliased!(S.A5, 1)); //a5.b1;
|
|
}
|
|
|
|
/// Pointers
|
|
unittest
|
|
{
|
|
int i = 0;
|
|
int* p = null;
|
|
assert(!p.doesPointTo(i));
|
|
p = &i;
|
|
assert( p.doesPointTo(i));
|
|
}
|
|
|
|
/// Structs and Unions
|
|
unittest
|
|
{
|
|
struct S
|
|
{
|
|
int v;
|
|
int* p;
|
|
}
|
|
int i;
|
|
auto s = S(0, &i);
|
|
|
|
//structs and unions "own" their members
|
|
//pointsTo will answer true if one of the members pointsTo.
|
|
assert(!s.doesPointTo(s.v)); //s.v is just v member of s, so not pointed.
|
|
assert( s.p.doesPointTo(i)); //i is pointed by s.p.
|
|
assert( s .doesPointTo(i)); //which means i is pointed by s itself.
|
|
|
|
//Unions will behave exactly the same. Points to will check each "member"
|
|
//individually, even if they share the same memory
|
|
}
|
|
|
|
/// Arrays (dynamic and static)
|
|
unittest
|
|
{
|
|
int i;
|
|
int[] slice = [0, 1, 2, 3, 4];
|
|
int[5] arr = [0, 1, 2, 3, 4];
|
|
int*[] slicep = [&i];
|
|
int*[1] arrp = [&i];
|
|
|
|
//A slice points to all of its members:
|
|
assert( slice.doesPointTo(slice[3]));
|
|
assert(!slice[0 .. 2].doesPointTo(slice[3])); //Object 3 is outside of the slice [0 .. 2]
|
|
|
|
//Note that a slice will not take into account what its members point to.
|
|
assert( slicep[0].doesPointTo(i));
|
|
assert(!slicep .doesPointTo(i));
|
|
|
|
//static arrays are objects that own their members, just like structs:
|
|
assert(!arr.doesPointTo(arr[0])); //arr[0] is just a member of arr, so not pointed.
|
|
assert( arrp[0].doesPointTo(i)); //i is pointed by arrp[0].
|
|
assert( arrp .doesPointTo(i)); //which means i is pointed by arrp itslef.
|
|
|
|
//Notice the difference between static and dynamic arrays:
|
|
assert(!arr .doesPointTo(arr[0]));
|
|
assert( arr[].doesPointTo(arr[0]));
|
|
assert( arrp .doesPointTo(i));
|
|
assert(!arrp[].doesPointTo(i));
|
|
}
|
|
|
|
/// Classes
|
|
unittest
|
|
{
|
|
class C
|
|
{
|
|
this(int* p){this.p = p;}
|
|
int* p;
|
|
}
|
|
int i;
|
|
C a = new C(&i);
|
|
C b = a;
|
|
//Classes are a bit particular, as they are treated like simple pointers
|
|
//to a class payload.
|
|
assert( a.p.doesPointTo(i)); //a.p points to i.
|
|
assert(!a .doesPointTo(i)); //Yet a itself does not point i.
|
|
|
|
//To check the class payload itself, iterate on its members:
|
|
()
|
|
{
|
|
foreach (index, _; FieldTypeTuple!C)
|
|
if (doesPointTo(a.tupleof[index], i))
|
|
return;
|
|
assert(0);
|
|
}();
|
|
|
|
//To check if a class points a specific payload, a direct memmory check can be done:
|
|
auto aLoc = cast(ubyte[__traits(classInstanceSize, C)]*) a;
|
|
assert(b.doesPointTo(*aLoc)); //b points to where a is pointing
|
|
}
|
|
|
|
unittest
|
|
{
|
|
struct S1 { int a; S1 * b; }
|
|
S1 a1;
|
|
S1 * p = &a1;
|
|
assert(doesPointTo(p, a1));
|
|
|
|
S1 a2;
|
|
a2.b = &a1;
|
|
assert(doesPointTo(a2, a1));
|
|
|
|
struct S3 { int[10] a; }
|
|
S3 a3;
|
|
auto a4 = a3.a[2 .. 3];
|
|
assert(doesPointTo(a4, a3));
|
|
|
|
auto a5 = new double[4];
|
|
auto a6 = a5[1 .. 2];
|
|
assert(!doesPointTo(a5, a6));
|
|
|
|
auto a7 = new double[3];
|
|
auto a8 = new double[][1];
|
|
a8[0] = a7;
|
|
assert(!doesPointTo(a8[0], a8[0]));
|
|
|
|
// don't invoke postblit on subobjects
|
|
{
|
|
static struct NoCopy { this(this) { assert(0); } }
|
|
static struct Holder { NoCopy a, b, c; }
|
|
Holder h;
|
|
cast(void)doesPointTo(h, h);
|
|
}
|
|
|
|
shared S3 sh3;
|
|
shared sh3sub = sh3.a[];
|
|
assert(doesPointTo(sh3sub, sh3));
|
|
|
|
int[] darr = [1, 2, 3, 4];
|
|
|
|
//dynamic arrays don't point to each other, or slices of themselves
|
|
assert(!doesPointTo(darr, darr));
|
|
assert(!doesPointTo(darr[0 .. 1], darr));
|
|
|
|
//But they do point their elements
|
|
foreach(i; 0 .. 4)
|
|
assert(doesPointTo(darr, darr[i]));
|
|
assert(doesPointTo(darr[0..3], darr[2]));
|
|
assert(!doesPointTo(darr[0..3], darr[3]));
|
|
}
|
|
|
|
unittest
|
|
{
|
|
//tests with static arrays
|
|
//Static arrays themselves are just objects, and don't really *point* to anything.
|
|
//They aggregate their contents, much the same way a structure aggregates its attributes.
|
|
//*However* The elements inside the static array may themselves point to stuff.
|
|
|
|
//Standard array
|
|
int[2] k;
|
|
assert(!doesPointTo(k, k)); //an array doesn't point to itself
|
|
//Technically, k doesn't point its elements, although it does alias them
|
|
assert(!doesPointTo(k, k[0]));
|
|
assert(!doesPointTo(k, k[1]));
|
|
//But an extracted slice will point to the same array.
|
|
assert(doesPointTo(k[], k));
|
|
assert(doesPointTo(k[], k[1]));
|
|
|
|
//An array of pointers
|
|
int*[2] pp;
|
|
int a;
|
|
int b;
|
|
pp[0] = &a;
|
|
assert( doesPointTo(pp, a)); //The array contains a pointer to a
|
|
assert(!doesPointTo(pp, b)); //The array does NOT contain a pointer to b
|
|
assert(!doesPointTo(pp, pp)); //The array does not point itslef
|
|
|
|
//A struct containing a static array of pointers
|
|
static struct S
|
|
{
|
|
int*[2] p;
|
|
}
|
|
S s;
|
|
s.p[0] = &a;
|
|
assert( doesPointTo(s, a)); //The struct contains an array that points a
|
|
assert(!doesPointTo(s, b)); //But doesn't point b
|
|
assert(!doesPointTo(s, s)); //The struct doesn't actually point itslef.
|
|
|
|
//An array containing structs that have pointers
|
|
static struct SS
|
|
{
|
|
int* p;
|
|
}
|
|
SS[2] ss = [SS(&a), SS(null)];
|
|
assert( doesPointTo(ss, a)); //The array contains a struct that points to a
|
|
assert(!doesPointTo(ss, b)); //The array doesn't contains a struct that points to b
|
|
assert(!doesPointTo(ss, ss)); //The array doesn't point itself.
|
|
}
|
|
|
|
|
|
unittest //Unions
|
|
{
|
|
int i;
|
|
union U //Named union
|
|
{
|
|
size_t asInt = 0;
|
|
int* asPointer;
|
|
}
|
|
struct S
|
|
{
|
|
union //Anonymous union
|
|
{
|
|
size_t asInt = 0;
|
|
int* asPointer;
|
|
}
|
|
}
|
|
|
|
U u;
|
|
S s;
|
|
assert(!doesPointTo(u, i));
|
|
assert(!doesPointTo(s, i));
|
|
assert(!mayPointTo(u, i));
|
|
assert(!mayPointTo(s, i));
|
|
|
|
u.asPointer = &i;
|
|
s.asPointer = &i;
|
|
assert(!doesPointTo(u, i));
|
|
assert(!doesPointTo(s, i));
|
|
assert( mayPointTo(u, i));
|
|
assert( mayPointTo(s, i));
|
|
|
|
u.asInt = cast(size_t)&i;
|
|
s.asInt = cast(size_t)&i;
|
|
assert(!doesPointTo(u, i));
|
|
assert(!doesPointTo(s, i));
|
|
assert( mayPointTo(u, i));
|
|
assert( mayPointTo(s, i));
|
|
}
|
|
|
|
unittest //Classes
|
|
{
|
|
int i;
|
|
static class A
|
|
{
|
|
int* p;
|
|
}
|
|
A a = new A, b = a;
|
|
assert(!doesPointTo(a, b)); //a does not point to b
|
|
a.p = &i;
|
|
assert(!doesPointTo(a, i)); //a does not point to i
|
|
}
|
|
unittest //alias this test
|
|
{
|
|
static int i;
|
|
static int j;
|
|
struct S
|
|
{
|
|
int* p;
|
|
@property int* foo(){return &i;}
|
|
alias foo this;
|
|
}
|
|
assert(is(S : int*));
|
|
S s = S(&j);
|
|
assert(!doesPointTo(s, i));
|
|
assert( doesPointTo(s, j));
|
|
assert( doesPointTo(cast(int*)s, i));
|
|
assert(!doesPointTo(cast(int*)s, j));
|
|
}
|
|
|
|
/*********************
|
|
* Thrown if errors that set $(D errno) occur.
|
|
*/
|
|
class ErrnoException : Exception
|
|
{
|
|
uint errno; // operating system error code
|
|
this(string msg, string file = null, size_t line = 0) @trusted
|
|
{
|
|
errno = .errno;
|
|
version (linux)
|
|
{
|
|
char[1024] buf = void;
|
|
auto s = std.c.string.strerror_r(errno, buf.ptr, buf.length);
|
|
}
|
|
else
|
|
{
|
|
auto s = std.c.string.strerror(errno);
|
|
}
|
|
super(msg~" ("~to!string(s)~")", file, line);
|
|
}
|
|
}
|
|
|
|
/++
|
|
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)
|
|
T1 = The type of the expression.
|
|
T2 = The return type of 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 exception, 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 throwRangeError() { throw new RangeError; }
|
|
assert(throwRangeError().ifThrown(0).collectException!RangeError() !is null);
|
|
assert(throwRangeError().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)));
|
|
}
|
|
|
|
version(unittest) package
|
|
@property void assertCTFEable(alias dg)()
|
|
{
|
|
static assert({ cast(void)dg(); return true; }());
|
|
cast(void)dg();
|
|
}
|