| // 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. | |
| $(SCRIPT inhibitQuickIndex = 1;) | |
| $(DIVC quickindex, | |
| $(BOOKTABLE, | |
| $(TR $(TH Category) $(TH Functions)) | |
| $(TR $(TD Assumptions) $(TD | |
| $(LREF assertNotThrown) | |
| $(LREF assertThrown) | |
| $(LREF assumeUnique) | |
| $(LREF assumeWontThrow) | |
| $(LREF mayPointTo) | |
| )) | |
| $(TR $(TD Enforce) $(TD | |
| $(LREF doesPointTo) | |
| $(LREF enforce) | |
| $(LREF errnoEnforce) | |
| )) | |
| $(TR $(TD Handlers) $(TD | |
| $(LREF collectException) | |
| $(LREF collectExceptionMsg) | |
| $(LREF ifThrown) | |
| $(LREF handle) | |
| )) | |
| $(TR $(TD Other) $(TD | |
| $(LREF basicExceptionCtors) | |
| $(LREF emptyExceptionMsg) | |
| $(LREF ErrnoException) | |
| $(LREF RangePrimitive) | |
| )) | |
| )) | |
| Copyright: Copyright Andrei Alexandrescu 2008-, Jonathan M Davis 2011-. | |
| License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
| Authors: $(HTTP erdani.org, Andrei Alexandrescu) and | |
| $(HTTP jmdavisprog.com, Jonathan M Davis) | |
| Source: $(PHOBOSSRC std/exception.d) | |
| +/ | |
| module std.exception; | |
| /// Synopis | |
| @system unittest | |
| { | |
| import core.stdc.stdlib : malloc, free; | |
| import std.algorithm.comparison : equal; | |
| import std.algorithm.iteration : map, splitter; | |
| import std.algorithm.searching : endsWith; | |
| import std.conv : ConvException, to; | |
| import std.range : front, retro; | |
| // use enforce like assert | |
| int a = 3; | |
| enforce(a > 2, "a needs to be higher than 2."); | |
| // enforce can throw a custom exception | |
| enforce!ConvException(a > 2, "a needs to be higher than 2."); | |
| // enforce will return it's input | |
| enum size = 42; | |
| auto memory = enforce(malloc(size), "malloc failed")[0 .. size]; | |
| scope(exit) free(memory.ptr); | |
| // collectException can be used to test for exceptions | |
| Exception e = collectException("abc".to!int); | |
| assert(e.file.endsWith("conv.d")); | |
| // and just for the exception message | |
| string msg = collectExceptionMsg("abc".to!int); | |
| assert(msg == "Unexpected 'a' when converting from type string to type int"); | |
| // assertThrown can be used to assert that an exception is thrown | |
| assertThrown!ConvException("abc".to!int); | |
| // ifThrown can be used to provide a default value if an exception is thrown | |
| assert("x".to!int().ifThrown(0) == 0); | |
| // handle is a more advanced version of ifThrown for ranges | |
| auto r = "12,1337z32,54".splitter(',').map!(a => to!int(a)); | |
| auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); | |
| assert(h.equal([12, 0, 54])); | |
| assertThrown!ConvException(h.retro.equal([54, 0, 12])); | |
| // basicExceptionCtors avoids the boilerplate when creating custom exceptions | |
| static class MeaCulpa : Exception | |
| { | |
| mixin basicExceptionCtors; | |
| } | |
| e = collectException((){throw new MeaCulpa("diagnostic message");}()); | |
| assert(e.msg == "diagnostic message"); | |
| assert(e.file == __FILE__); | |
| assert(e.line == __LINE__ - 3); | |
| // assumeWontThrow can be used to cast throwing code into `nothrow` | |
| void exceptionFreeCode() nothrow | |
| { | |
| // auto-decoding only throws if an invalid UTF char is given | |
| assumeWontThrow("abc".front); | |
| } | |
| // assumeUnique can be used to cast mutable instance to an `immutable` one | |
| // use with care | |
| char[] str = " mutable".dup; | |
| str[0 .. 2] = "im"; | |
| immutable res = assumeUnique(str); | |
| assert(res == "immutable"); | |
| } | |
| import std.range.primitives; | |
| import std.traits; | |
| /++ | |
| Asserts that the given expression does $(I not) throw the given type | |
| of `Throwable`. If a `Throwable` of the given type is thrown, | |
| it is caught and does not escape assertNotThrown. Rather, an | |
| `AssertError` is thrown. However, any other `Throwable`s will escape. | |
| Params: | |
| T = The `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 `__FILE__`. | |
| line = The line where the error occurred. | |
| Defaults to `__LINE__`. | |
| Throws: | |
| `AssertError` if the given `Throwable` is thrown. | |
| Returns: | |
| the result of `expression`. | |
| +/ | |
| auto assertNotThrown(T : Throwable = Exception, E) | |
| (lazy E expression, | |
| string msg = null, | |
| string file = __FILE__, | |
| size_t line = __LINE__) | |
| { | |
| import core.exception : AssertError; | |
| try | |
| { | |
| return expression(); | |
| } | |
| catch (T t) | |
| { | |
| immutable message = msg.length == 0 ? t.msg : msg; | |
| immutable tail = message.length == 0 ? "." : ": " ~ message; | |
| throw new AssertError("assertNotThrown failed: " ~ T.stringof ~ " was thrown" ~ tail, file, line, t); | |
| } | |
| } | |
| /// | |
| @system unittest | |
| { | |
| import core.exception : AssertError; | |
| import std.string; | |
| assertNotThrown!StringException(enforce!StringException(true, "Error!")); | |
| //Exception is the default. | |
| assertNotThrown(enforce!StringException(true, "Error!")); | |
| assert(collectExceptionMsg!AssertError(assertNotThrown!StringException( | |
| enforce!StringException(false, "Error!"))) == | |
| `assertNotThrown failed: StringException was thrown: Error!`); | |
| } | |
| @system unittest | |
| { | |
| import core.exception : AssertError; | |
| import std.string; | |
| assert(collectExceptionMsg!AssertError(assertNotThrown!StringException( | |
| enforce!StringException(false, ""), "Error!")) == | |
| `assertNotThrown failed: StringException was thrown: Error!`); | |
| assert(collectExceptionMsg!AssertError(assertNotThrown!StringException( | |
| enforce!StringException(false, ""))) == | |
| `assertNotThrown failed: StringException was thrown.`); | |
| assert(collectExceptionMsg!AssertError(assertNotThrown!StringException( | |
| enforce!StringException(false, ""), "")) == | |
| `assertNotThrown failed: StringException was thrown.`); | |
| } | |
| @system unittest | |
| { | |
| import core.exception : AssertError; | |
| static noreturn throwEx(Throwable t) { throw t; } | |
| bool nothrowEx() { return true; } | |
| try | |
| { | |
| assert(assertNotThrown!Exception(nothrowEx())); | |
| } | |
| catch (AssertError) assert(0); | |
| try | |
| { | |
| assert(assertNotThrown!Exception(nothrowEx(), "It's a message")); | |
| } | |
| catch (AssertError) assert(0); | |
| try | |
| { | |
| assert(assertNotThrown!AssertError(nothrowEx())); | |
| } | |
| catch (AssertError) assert(0); | |
| try | |
| { | |
| assert(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 `Throwable`. | |
| The `Throwable` is caught and does not escape assertThrown. However, | |
| any other `Throwable`s $(I will) escape, and if no `Throwable` | |
| of the given type is thrown, then an `AssertError` is thrown. | |
| Params: | |
| T = The `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 `__FILE__`. | |
| line = The line where the error occurred. | |
| Defaults to `__LINE__`. | |
| Throws: | |
| `AssertError` if the given `Throwable` is not thrown. | |
| +/ | |
| void assertThrown(T : Throwable = Exception, E) | |
| (lazy E expression, | |
| string msg = null, | |
| string file = __FILE__, | |
| size_t line = __LINE__) | |
| { | |
| import core.exception : AssertError; | |
| try | |
| expression(); | |
| catch (T) | |
| return; | |
| static if (!is(immutable E == immutable noreturn)) | |
| throw new AssertError("assertThrown failed: No " ~ T.stringof ~ " was thrown" | |
| ~ (msg.length == 0 ? "." : ": ") ~ msg, | |
| file, line); | |
| } | |
| /// | |
| @system unittest | |
| { | |
| import core.exception : AssertError; | |
| import std.string; | |
| assertThrown!StringException(enforce!StringException(false, "Error!")); | |
| //Exception is the default. | |
| assertThrown(enforce!StringException(false, "Error!")); | |
| assert(collectExceptionMsg!AssertError(assertThrown!StringException( | |
| enforce!StringException(true, "Error!"))) == | |
| `assertThrown failed: No StringException was thrown.`); | |
| } | |
| @system unittest | |
| { | |
| import core.exception : AssertError; | |
| static noreturn 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); | |
| } | |
| } | |
| /++ | |
| Enforces that the given value is true. | |
| If the given value is false, an exception is thrown. | |
| The | |
| $(UL | |
| $(LI `msg` - error message as a `string`) | |
| $(LI `dg` - custom delegate that return a string and is only called if an exception occurred) | |
| $(LI `ex` - custom exception to be thrown. It is `lazy` and is only created if an exception occurred) | |
| ) | |
| Params: | |
| value = The value to test. | |
| E = Exception type to throw if the value evaluates to false. | |
| msg = The error message to put in the exception if it is thrown. | |
| dg = The delegate to be called if the value evaluates to false. | |
| ex = The exception to throw if the value evaluates to false. | |
| file = The source file of the caller. | |
| line = The line number of the caller. | |
| Returns: `value`, if `cast(bool) value` is true. Otherwise, | |
| depending on the chosen overload, `new Exception(msg)`, `dg()` or `ex` is thrown. | |
| $(PANEL | |
| $(NOTE `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 `assert` is for.) | |
| Do not use | |
| `enforce` inside of contracts (i.e. inside of `in` and `out` | |
| blocks and `invariant`s), because contracts are compiled out when | |
| compiling with $(I -release). | |
| ) | |
| If a delegate is passed, the safety and purity of this function are inferred | |
| from `Dg`'s safety and purity. | |
| +/ | |
| template enforce(E : Throwable = Exception) | |
| if (is(typeof(new E("", string.init, size_t.init)) : Throwable) || | |
| is(typeof(new E(string.init, size_t.init)) : Throwable)) | |
| { | |
| /// | |
| 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!E(file, line, msg); | |
| return value; | |
| } | |
| } | |
| /// ditto | |
| 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; | |
| } | |
| /// ditto | |
| T enforce(T)(T value, lazy Throwable ex) | |
| { | |
| if (!value) throw ex(); | |
| return value; | |
| } | |
| /// | |
| @system unittest | |
| { | |
| import core.stdc.stdlib : malloc, free; | |
| import std.conv : ConvException, to; | |
| // use enforce like assert | |
| int a = 3; | |
| enforce(a > 2, "a needs to be higher than 2."); | |
| // enforce can throw a custom exception | |
| enforce!ConvException(a > 2, "a needs to be higher than 2."); | |
| // enforce will return it's input | |
| enum size = 42; | |
| auto memory = enforce(malloc(size), "malloc failed")[0 .. size]; | |
| scope(exit) free(memory.ptr); | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| assertNotThrown(enforce(true, new Exception("this should not be thrown"))); | |
| assertThrown(enforce(false, new Exception("this should be thrown"))); | |
| } | |
| /// | |
| @safe 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); | |
| } | |
| } | |
| /// Alias your own enforce function | |
| @safe unittest | |
| { | |
| import std.conv : ConvException; | |
| alias convEnforce = enforce!ConvException; | |
| assertNotThrown(convEnforce(true)); | |
| assertThrown!ConvException(convEnforce(false, "blah")); | |
| } | |
| private noreturn bailOut(E : Throwable = Exception)(string file, size_t line, scope const(char)[] msg) | |
| { | |
| static if (is(typeof(new E(string.init, string.init, size_t.init)))) | |
| { | |
| throw new E(msg ? msg.idup : "Enforcement failed", file, line); | |
| } | |
| else static if (is(typeof(new E(string.init, size_t.init)))) | |
| { | |
| throw new E(file, line); | |
| } | |
| else | |
| { | |
| static assert(0, "Expected this(string, string, size_t) or this(string, size_t)" ~ | |
| " constructor for " ~ __traits(identifier, E)); | |
| } | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=10510 | |
| @safe unittest | |
| { | |
| extern(C) void cFoo() { } | |
| enforce(false, &cFoo); | |
| } | |
| // purity and safety inference test | |
| @system unittest | |
| { | |
| static foreach (EncloseSafe; [false, true]) | |
| static foreach (EnclosePure; [false, true]) | |
| { | |
| static foreach (BodySafe; [false, true]) | |
| static foreach (BodyPure; [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 https://issues.dlang.org/show_bug.cgi?id=8637 | |
| @system 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__); } | |
| } | |
| enforce!E1(s); | |
| enforce!E2(s); | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=14685 | |
| @safe unittest | |
| { | |
| class E : Exception | |
| { | |
| this() { super("Not found"); } | |
| } | |
| static assert(!__traits(compiles, { enforce!E(false); })); | |
| } | |
| /++ | |
| Enforces that the given value is true, throwing an `ErrnoException` if it | |
| is not. | |
| Params: | |
| value = The value to test. | |
| msg = The message to include in the `ErrnoException` if it is thrown. | |
| Returns: `value`, if `cast(bool) value` is true. Otherwise, | |
| $(D new ErrnoException(msg)) is thrown. It is assumed that the last | |
| operation set `errno` to an error code corresponding with the failed | |
| condition. | |
| +/ | |
| alias errnoEnforce = enforce!ErrnoException; | |
| /// | |
| @system unittest | |
| { | |
| import core.stdc.stdio : fclose, fgets, fopen; | |
| import std.file : thisExePath; | |
| import std.string : toStringz; | |
| auto f = fopen(thisExePath.toStringz, "r").errnoEnforce; | |
| scope(exit) fclose(f); | |
| char[100] buf; | |
| auto line = fgets(buf.ptr, buf.length, f); | |
| enforce(line !is null); // expect a non-empty line | |
| } | |
| /++ | |
| Catches and returns the exception thrown from the given expression. | |
| If no exception is thrown, then null is returned and `result` is | |
| set to the result of the expression. | |
| Note that while `collectException` $(I can) be used to collect any | |
| `Throwable` and not just `Exception`s, it is generally ill-advised to | |
| catch anything that is neither an `Exception` nor a type derived from | |
| `Exception`. So, do not use `collectException` to collect | |
| non-`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; | |
| } | |
| // Avoid "statement not reachable" warning | |
| static if (!is(immutable E == immutable noreturn)) | |
| return null; | |
| } | |
| /// | |
| @system unittest | |
| { | |
| int b; | |
| int foo() { throw new Exception("blah"); } | |
| assert(collectException(foo(), b)); | |
| version (D_NoBoundsChecks) {} | |
| else | |
| { | |
| // check for out of bounds error | |
| 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. `E` can be | |
| `void`. | |
| Note that while `collectException` $(I can) be used to collect any | |
| `Throwable` and not just `Exception`s, it is generally ill-advised to | |
| catch anything that is neither an `Exception` nor a type derived from | |
| `Exception`. So, do not use `collectException` to collect | |
| non-`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; | |
| } | |
| // Avoid "statement not reachable" warning | |
| static if (!is(immutable E == immutable noreturn)) | |
| return null; | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| int foo() { throw new Exception("blah"); } | |
| assert(collectException(foo()).msg == "blah"); | |
| } | |
| /++ | |
| 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. `E` can be `void`. | |
| If an exception is thrown but it has an empty message, then | |
| `emptyExceptionMsg` is returned. | |
| Note that while `collectExceptionMsg` $(I can) be used to collect any | |
| `Throwable` and not just `Exception`s, it is generally ill-advised to | |
| catch anything that is neither an `Exception` nor a type derived from | |
| `Exception`. So, do not use `collectExceptionMsg` to collect | |
| non-`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) | |
| { | |
| import std.array : empty; | |
| try | |
| { | |
| expression(); | |
| // Avoid "statement not reachable" warning | |
| static if (!is(immutable E == immutable noreturn)) | |
| return cast(string) null; | |
| } | |
| catch (T e) | |
| return e.msg.empty ? emptyExceptionMsg : e.msg; | |
| } | |
| /// | |
| @safe 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>"; | |
| // https://issues.dlang.org/show_bug.cgi?id=22364 | |
| @system unittest | |
| { | |
| static noreturn foo() { throw new Exception(""); } | |
| const ex = collectException!(Exception, noreturn)(foo()); | |
| assert(ex); | |
| const msg = collectExceptionMsg!(Exception, noreturn)(foo()); | |
| assert(msg); | |
| noreturn n; | |
| // Triggers a backend assertion failure | |
| // collectException!(Exception, noreturn)(foo(), n); | |
| static assert(__traits(compiles, collectException!(Exception, noreturn)(foo(), n))); | |
| } | |
| /** | |
| Casts a mutable array to an immutable array in an idiomatic | |
| manner. Technically, `assumeUnique` just inserts a cast, | |
| but its name documents assumptions on the part of the | |
| caller. `assumeUnique(arr)` should only be called when | |
| there are no more active mutable aliases to elements of $(D | |
| arr). To strengthen this assumption, `assumeUnique(arr)` | |
| also clears `arr` before returning. Essentially $(D | |
| assumeUnique(arr)) indicates commitment from the caller that there | |
| is no more mutable access to any of `arr`'s elements | |
| (transitively), and that all future accesses will be done through | |
| the immutable array returned by `assumeUnique`. | |
| Typically, `assumeUnique` is used to return arrays from | |
| functions that have allocated and built them. | |
| Params: | |
| array = The array to cast to immutable. | |
| Returns: The immutable array. | |
| Example: | |
| $(RUNNABLE_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 `result` | |
| was private to `letters` and the memory it referenced can no longer be written to | |
| after the function returns. The following example shows an | |
| incorrect use of `assumeUnique`. | |
| Bad: | |
| $(RUNNABLE_EXAMPLE | |
| ---- | |
| 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 modifies the | |
| returned array that the previous caller considered immutable. To obtain an | |
| immutable array from the writable array `buffer`, replace | |
| the last line with: | |
| ---- | |
| return to!(string)(sneaky); // not that sneaky anymore | |
| ---- | |
| The `to` call will duplicate the array appropriately. | |
| $(PANEL | |
| $(NOTE Checking for uniqueness during compilation is | |
| possible in certain cases, especially when a function is | |
| marked (or inferred) as `pure`. The following example does not | |
| need to call `assumeUnique` because the compiler can infer the | |
| uniqueness of the array in the pure function:) | |
| $(RUNNABLE_EXAMPLE | |
| ---- | |
| static 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 | |
| $(HTTP www.cs.cmu.edu/~aldrich/papers/aldrich-dissertation.pdf, ArchJava) | |
| language. | |
| ) | |
| The downside of using `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 `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; | |
| } | |
| /// ditto | |
| immutable(T[U]) assumeUnique(T, U)(ref T[U] array) pure nothrow | |
| { | |
| auto result = cast(immutable(T[U])) array; | |
| array = null; | |
| return result; | |
| } | |
| /// | |
| @system unittest | |
| { | |
| int[] arr = new int[1]; | |
| auto arr1 = arr.assumeUnique; | |
| static assert(is(typeof(arr1) == immutable(int)[])); | |
| assert(arr == null); | |
| assert(arr1 == [0]); | |
| } | |
| /// | |
| @system unittest | |
| { | |
| int[string] arr = ["a":1]; | |
| auto arr1 = arr.assumeUnique; | |
| static assert(is(typeof(arr1) == immutable(int[string]))); | |
| assert(arr == null); | |
| assert(arr1.keys == ["a"]); | |
| } | |
| /** | |
| * Wraps a possibly-throwing expression in a `nothrow` wrapper so that it | |
| * can be called by a `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 `expr`. If it turns out | |
| * that the expression $(I does) throw at runtime, the wrapper will throw an | |
| * `AssertError`. | |
| * | |
| * (Note that `Throwable` objects such as `AssertError` that do not | |
| * subclass `Exception` may be thrown even from `nothrow` functions, | |
| * since they are considered to be serious runtime problems that cannot be | |
| * recovered from.) | |
| * | |
| * Params: | |
| * expr = The expression asserted not to throw. | |
| * msg = The message to include in the `AssertError` if the assumption turns | |
| * out to be false. | |
| * file = The source file name of the caller. | |
| * line = The line number of the caller. | |
| * | |
| * Returns: | |
| * The value of `expr`, if any. | |
| */ | |
| T assumeWontThrow(T)(lazy T expr, | |
| string msg = null, | |
| string file = __FILE__, | |
| size_t line = __LINE__) nothrow | |
| { | |
| import core.exception : AssertError; | |
| try | |
| { | |
| return expr; | |
| } | |
| catch (Exception e) | |
| { | |
| import std.range.primitives : empty; | |
| immutable tail = msg.empty ? "." : ": " ~ msg; | |
| throw new AssertError("assumeWontThrow failed: Expression did throw" ~ | |
| tail, file, line); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import std.math.algebraic : 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); | |
| } | |
| @system unittest | |
| { | |
| import core.exception : AssertError; | |
| void alwaysThrows() | |
| { | |
| throw new Exception("I threw up"); | |
| } | |
| void bad() nothrow | |
| { | |
| assumeWontThrow(alwaysThrows()); | |
| } | |
| assertThrown!AssertError(bad()); | |
| } | |
| /** | |
| Checks whether a given source object contains pointers or references to a given | |
| target object. | |
| Params: | |
| source = The source object | |
| target = The target object | |
| Bugs: | |
| The function is explicitly annotated `@nogc` because inference could fail, | |
| see $(LINK2 https://issues.dlang.org/show_bug.cgi?id=17084, Bugzilla issue 17084). | |
| Returns: `true` if `source`'s representation embeds a pointer | |
| that points to `target`'s representation or somewhere inside | |
| it. | |
| If `source` is or contains a dynamic array, then, then these functions will check | |
| if there is overlap between the dynamic array and `target`'s representation. | |
| If `source` is a class, then it will be handled as a pointer. | |
| If `target` is a pointer, a dynamic array or a class, then these functions will only | |
| check if `source` points to `target`, $(I not) what `target` references. | |
| If `source` is or contains a union or `void[n]`, then there may be either false positives or | |
| false negatives: | |
| `doesPointTo` will return `true` if it is absolutely certain | |
| `source` points to `target`. It may produce false negatives, but never | |
| false positives. This function should be prefered when trying to validate | |
| input data. | |
| `mayPointTo` will return `false` if it is absolutely certain | |
| `source` does not point to `target`. It may produce false positives, but never | |
| false negatives. This function should be prefered for defensively choosing a | |
| code path. | |
| Note: Evaluating $(D doesPointTo(x, x)) checks whether `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) @nogc @trusted pure nothrow | |
| if (__traits(isRef, source) || isDynamicArray!S || | |
| is(S == U*, U) || is(S == class)) | |
| { | |
| static if (is(S == U*, U) || is(S == class) || is(S == interface)) | |
| { | |
| const m = *cast(void**) &source; | |
| const b = cast(void*) ⌖ | |
| const 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) | |
| { | |
| static if (!is(S == void[n], size_t n)) | |
| { | |
| foreach (ref s; source) | |
| if (doesPointTo(s, target)) return true; | |
| } | |
| return false; | |
| } | |
| else static if (isDynamicArray!S) | |
| { | |
| import std.array : overlap; | |
| return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0; | |
| } | |
| else | |
| { | |
| return false; | |
| } | |
| } | |
| // for shared objects | |
| /// ditto | |
| 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); | |
| } | |
| /// ditto | |
| bool mayPointTo(S, T, Tdummy=void)(auto ref const S source, ref const T target) @trusted pure nothrow | |
| if (__traits(isRef, source) || isDynamicArray!S || | |
| is(S == U*, U) || is(S == class)) | |
| { | |
| static if (is(S == U*, U) || is(S == class) || is(S == interface)) | |
| { | |
| const m = *cast(void**) &source; | |
| const b = cast(void*) ⌖ | |
| const 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) | |
| { | |
| static if (is(S == void[n], size_t n)) | |
| { | |
| static if (n >= (void[]).sizeof) | |
| { | |
| // could contain a slice, which could point at anything. | |
| // But a void[N] that is all 0 cannot point anywhere | |
| import std.algorithm.searching : any; | |
| if (__ctfe || any(cast(ubyte[]) source[])) | |
| return true; | |
| } | |
| else static if (n >= (void*).sizeof) | |
| { | |
| // Reinterpreting cast is impossible during ctfe | |
| if (__ctfe) | |
| return true; | |
| // Only check for properly aligned pointers | |
| enum al = (void*).alignof - 1; | |
| const base = cast(size_t) &source; | |
| const alBase = (base + al) & ~al; | |
| if ((n - (alBase - base)) >= (void*).sizeof && | |
| mayPointTo(*(cast(void**) alBase), target)) | |
| return true; | |
| } | |
| } | |
| else | |
| { | |
| foreach (size_t i; 0 .. S.length) | |
| if (mayPointTo(source[i], target)) return true; | |
| } | |
| return false; | |
| } | |
| else static if (isDynamicArray!S) | |
| { | |
| import std.array : overlap; | |
| return overlap(cast(void[]) source, cast(void[])(&target)[0 .. 1]).length != 0; | |
| } | |
| else | |
| { | |
| return false; | |
| } | |
| } | |
| // for shared objects | |
| /// ditto | |
| 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); | |
| } | |
| /// Pointers | |
| @system unittest | |
| { | |
| int i = 0; | |
| int* p = null; | |
| assert(!p.doesPointTo(i)); | |
| p = &i; | |
| assert( p.doesPointTo(i)); | |
| } | |
| /// Structs and Unions | |
| @system 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) | |
| @system unittest | |
| { | |
| int i; | |
| // trick the compiler when initializing slice | |
| // https://issues.dlang.org/show_bug.cgi?id=18637 | |
| int* p = &i; | |
| int[] slice = [0, 1, 2, 3, 4]; | |
| int[5] arr = [0, 1, 2, 3, 4]; | |
| int*[] slicep = [p]; | |
| 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 | |
| // itself. | |
| // 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 | |
| @system 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: | |
| () | |
| { | |
| import std.traits : Fields; | |
| foreach (index, _; Fields!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 | |
| } | |
| version (StdUnittest) | |
| { | |
| // https://issues.dlang.org/show_bug.cgi?id=17084 | |
| // the bug doesn't happen if these declarations are in the unittest block | |
| // (static or not). | |
| private struct Page17084 | |
| { | |
| URL17084 url; | |
| int opCmp(P)(P) { return 0; } | |
| int opCmp(P)(shared(P)) shared { return 0; } | |
| } | |
| private struct URL17084 | |
| { | |
| int[] queryParams; | |
| string toString()() const { return ""; } | |
| alias toString this; | |
| } | |
| } | |
| // https://issues.dlang.org/show_bug.cgi?id=17084 | |
| @system unittest | |
| { | |
| import std.algorithm.sorting : sort; | |
| Page17084[] s; | |
| sort(s); | |
| shared(Page17084)[] p; | |
| sort(p); | |
| } | |
| @system 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])); | |
| } | |
| @system 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. | |
| // https://issues.dlang.org/show_bug.cgi?id=20426 | |
| align((void*).alignof) void[32] voidArr = void; | |
| (cast(void*[]) voidArr[])[] = null; // Ensure no false pointers | |
| // zeroed void ranges can't point at anything | |
| assert(!mayPointTo(voidArr, a)); | |
| assert(!mayPointTo(voidArr, b)); | |
| *cast(void**) &voidArr[16] = &a; // Pointers should be found | |
| alias SA = void[size_t.sizeof + 3]; | |
| SA *smallArr1 = cast(SA*)&voidArr; | |
| SA *smallArr2 = cast(SA*)&(voidArr[16]); | |
| // But it should only consider properly aligned pointers | |
| // Write single bytes to avoid issues due to misaligned writes | |
| void*[1] tmp = [&b]; | |
| (cast(ubyte[]) voidArr[3 .. 3 + (void*).sizeof])[] = cast(ubyte[]) tmp[]; | |
| assert( mayPointTo(*smallArr2, a)); | |
| assert(!mayPointTo(*smallArr1, b)); | |
| assert(!doesPointTo(voidArr, a)); // Value might be a false pointer | |
| assert(!doesPointTo(voidArr, b)); | |
| SA *smallArr3 = cast(SA *) &voidArr[13]; // Works for weird sizes/alignments | |
| assert( mayPointTo(*smallArr3, a)); | |
| assert(!mayPointTo(*smallArr3, b)); | |
| assert(!doesPointTo(*smallArr3, a)); | |
| assert(!doesPointTo(*smallArr3, b)); | |
| auto v3 = cast(void[3]*) &voidArr[16]; // Arrays smaller than pointers are ignored | |
| assert(!mayPointTo(*v3, a)); | |
| assert(!mayPointTo(*v3, b)); | |
| assert(!doesPointTo(*v3, a)); | |
| assert(!doesPointTo(*v3, b)); | |
| assert(mayPointTo(voidArr, a)); // slice-contiaining void[N] might point at anything | |
| assert(mayPointTo(voidArr, b)); | |
| static assert(() { | |
| void[16] arr1 = void; | |
| void[size_t.sizeof] arr2 = void; | |
| int var; | |
| return mayPointTo(arr1, var) && !doesPointTo(arr1, var) && | |
| mayPointTo(arr2, var) && !doesPointTo(arr2, var); | |
| }()); | |
| } | |
| @system 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)); | |
| } | |
| @system 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 | |
| } | |
| @safe 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)); | |
| } | |
| /+ | |
| Returns true if the field at index `i` in $(D T) shares its address with another field. | |
| Note: This does not merely 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; | |
| } | |
| // | |
| @safe 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; | |
| } | |
| version (CRuntime_Glibc) version = GNU_STRERROR; | |
| version (CRuntime_UClibc) version = GNU_STRERROR; | |
| package string errnoString(int errno) nothrow @trusted | |
| { | |
| import core.stdc.string : strlen; | |
| version (GNU_STRERROR) | |
| { | |
| import core.stdc.string : strerror_r; | |
| char[1024] buf = void; | |
| auto s = strerror_r(errno, buf.ptr, buf.length); | |
| } | |
| else version (Posix) | |
| { | |
| // XSI-compliant | |
| import core.stdc.string : strerror_r; | |
| char[1024] buf = void; | |
| const(char)* s; | |
| if (strerror_r(errno, buf.ptr, buf.length) == 0) | |
| s = buf.ptr; | |
| else | |
| return "Unknown error"; | |
| } | |
| else | |
| { | |
| import core.stdc.string : strerror; | |
| auto s = strerror(errno); | |
| } | |
| return s[0 .. s.strlen].idup; | |
| } | |
| /********************* | |
| * Thrown if errors that set `errno` occur. | |
| */ | |
| class ErrnoException : Exception | |
| { | |
| /// Operating system error code. | |
| final @property uint errno() nothrow pure scope @nogc @safe { return _errno; } | |
| private uint _errno; | |
| /// Localized error message generated through $(REF strerror_r, core,stdc,string) or $(REF strerror, core,stdc,string). | |
| final @property string errnoMsg() nothrow pure scope @nogc @safe { return _errnoMsg; } | |
| private string _errnoMsg; | |
| /// Constructor which takes an error message. The current global $(REF errno, core,stdc,errno) value is used as error code. | |
| this(string msg, string file = null, size_t line = 0) @safe | |
| { | |
| import core.stdc.errno : errno; | |
| this(msg, errno, file, line); | |
| } | |
| /// Constructor which takes an error message and error code. | |
| this(string msg, int errno, string file = null, size_t line = 0) @safe | |
| { | |
| _errno = errno; | |
| _errnoMsg = errnoString(errno); | |
| super(msg ~ " (" ~ errnoMsg ~ ")", file, line); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| import core.stdc.errno : EAGAIN; | |
| auto ex = new ErrnoException("oh no", EAGAIN); | |
| assert(ex.errno == EAGAIN); | |
| } | |
| /// errno is used by default if no explicit error code is provided | |
| @safe unittest | |
| { | |
| import core.stdc.errno : errno, EAGAIN; | |
| auto old = errno; | |
| scope(exit) errno = old; | |
| // fake that errno got set by the callee | |
| errno = EAGAIN; | |
| auto ex = new ErrnoException("oh no"); | |
| assert(ex.errno == EAGAIN); | |
| } | |
| /++ | |
| ML-style functional exception handling. Runs the supplied expression and | |
| returns its result. If the expression throws a `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 `Throwable`s to catch. Defaults to `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. | |
| Returns: | |
| expression, if it does not throw. Otherwise, returns the result of | |
| errorHandler. | |
| +/ | |
| //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); | |
| } | |
| } | |
| /// Revert to a default value upon an error: | |
| @safe unittest | |
| { | |
| import std.conv : to; | |
| assert("x".to!int.ifThrown(0) == 0); | |
| } | |
| /** | |
| Chain multiple calls to ifThrown, each capturing errors from the | |
| entire preceding expression. | |
| */ | |
| @safe unittest | |
| { | |
| import std.conv : ConvException, to; | |
| string s = "true"; | |
| assert(s.to!int.ifThrown(cast(int) s.to!double) | |
| .ifThrown(cast(int) s.to!bool) == 1); | |
| s = "2.0"; | |
| assert(s.to!int.ifThrown(cast(int) s.to!double) | |
| .ifThrown(cast(int) s.to!bool) == 2); | |
| // Respond differently to different types of errors | |
| alias orFallback = (lazy a) => a.ifThrown!ConvException("not a number") | |
| .ifThrown!Exception("number too small"); | |
| assert(orFallback(enforce("x".to!int < 1).to!string) == "not a number"); | |
| assert(orFallback(enforce("2".to!int < 1).to!string) == "number too small"); | |
| } | |
| /** | |
| 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. | |
| */ | |
| @safe unittest | |
| { | |
| // 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. | |
| @system unittest | |
| { | |
| import std.format : format; | |
| assert("%s".format.ifThrown!Exception(e => typeid(e).name) == "std.format.FormatException"); | |
| } | |
| //Verify Examples | |
| @system unittest | |
| { | |
| import std.conv; | |
| import std.string; | |
| //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 => typeid(e).name) == "std.format.FormatException"); | |
| } | |
| @system unittest | |
| { | |
| import core.exception; | |
| import std.conv; | |
| import std.string; | |
| //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 (StdUnittest) package | |
| void assertCTFEable(alias dg)() | |
| { | |
| static assert({ cast(void) dg(); return true; }()); | |
| cast(void) dg(); | |
| } | |
| /** This `enum` is used to select the primitives of the range to handle by the | |
| $(LREF handle) range wrapper. The values of the `enum` can be `OR`'d to | |
| select multiple primitives to be handled. | |
| `RangePrimitive.access` is a shortcut for the access primitives; `front`, | |
| `back` and `opIndex`. | |
| `RangePrimitive.pop` is a shortcut for the mutating primitives; | |
| `popFront` and `popBack`. | |
| */ | |
| enum RangePrimitive | |
| { | |
| front = 0b00_0000_0001, /// | |
| back = 0b00_0000_0010, /// Ditto | |
| popFront = 0b00_0000_0100, /// Ditto | |
| popBack = 0b00_0000_1000, /// Ditto | |
| empty = 0b00_0001_0000, /// Ditto | |
| save = 0b00_0010_0000, /// Ditto | |
| length = 0b00_0100_0000, /// Ditto | |
| opDollar = 0b00_1000_0000, /// Ditto | |
| opIndex = 0b01_0000_0000, /// Ditto | |
| opSlice = 0b10_0000_0000, /// Ditto | |
| access = front | back | opIndex, /// Ditto | |
| pop = popFront | popBack, /// Ditto | |
| } | |
| /// | |
| pure @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.algorithm.iteration : map, splitter; | |
| import std.conv : to, ConvException; | |
| auto s = "12,1337z32,54,2,7,9,1z,6,8"; | |
| // The next line composition will throw when iterated | |
| // as some elements of the input do not convert to integer | |
| auto r = s.splitter(',').map!(a => to!int(a)); | |
| // Substitute 0 for cases of ConvException | |
| auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); | |
| assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8])); | |
| } | |
| /// | |
| pure @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.range : retro; | |
| import std.utf : UTFException; | |
| auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit | |
| auto handled = str.handle!(UTFException, RangePrimitive.access, | |
| (e, r) => ' '); // Replace invalid code points with spaces | |
| assert(handled.equal("hello world")); // `front` is handled, | |
| assert(handled.retro.equal("dlrow olleh")); // as well as `back` | |
| } | |
| /** Handle exceptions thrown from range primitives. | |
| Use the $(LREF RangePrimitive) enum to specify which primitives to _handle. | |
| Multiple range primitives can be handled at once by using the `OR` operator | |
| or the pseudo-primitives `RangePrimitive.access` and `RangePrimitive.pop`. | |
| All handled primitives must have return types or values compatible with the | |
| user-supplied handler. | |
| Params: | |
| E = The type of `Throwable` to _handle. | |
| primitivesToHandle = Set of range primitives to _handle. | |
| handler = The callable that is called when a handled primitive throws a | |
| `Throwable` of type `E`. The handler must accept arguments of | |
| the form $(D E, ref IRange) and its return value is used as the primitive's | |
| return value whenever `E` is thrown. For `opIndex`, the handler can | |
| optionally recieve a third argument; the index that caused the exception. | |
| input = The range to _handle. | |
| Returns: A wrapper `struct` that preserves the range interface of `input`. | |
| Note: | |
| Infinite ranges with slicing support must return an instance of | |
| $(REF Take, std,range) when sliced with a specific lower and upper | |
| bound (see $(REF hasSlicing, std,range,primitives)); `handle` deals with | |
| this by `take`ing 0 from the return value of the handler function and | |
| returning that when an exception is caught. | |
| */ | |
| auto handle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Range)(Range input) | |
| if (isInputRange!Range) | |
| { | |
| static struct Handler | |
| { | |
| private Range range; | |
| static if (isForwardRange!Range) | |
| { | |
| @property typeof(this) save() | |
| { | |
| static if (primitivesToHandle & RangePrimitive.save) | |
| { | |
| try | |
| { | |
| return typeof(this)(range.save); | |
| } | |
| catch (E exception) | |
| { | |
| return typeof(this)(handler(exception, this.range)); | |
| } | |
| } | |
| else | |
| return typeof(this)(range.save); | |
| } | |
| } | |
| static if (isInfinite!Range) | |
| { | |
| enum bool empty = false; | |
| } | |
| else | |
| { | |
| @property bool empty() | |
| { | |
| static if (primitivesToHandle & RangePrimitive.empty) | |
| { | |
| try | |
| { | |
| return this.range.empty; | |
| } | |
| catch (E exception) | |
| { | |
| return handler(exception, this.range); | |
| } | |
| } | |
| else | |
| return this.range.empty; | |
| } | |
| } | |
| @property auto ref front() | |
| { | |
| static if (primitivesToHandle & RangePrimitive.front) | |
| { | |
| try | |
| { | |
| return this.range.front; | |
| } | |
| catch (E exception) | |
| { | |
| return handler(exception, this.range); | |
| } | |
| } | |
| else | |
| return this.range.front; | |
| } | |
| void popFront() | |
| { | |
| static if (primitivesToHandle & RangePrimitive.popFront) | |
| { | |
| try | |
| { | |
| this.range.popFront(); | |
| } | |
| catch (E exception) | |
| { | |
| handler(exception, this.range); | |
| } | |
| } | |
| else | |
| this.range.popFront(); | |
| } | |
| static if (isBidirectionalRange!Range) | |
| { | |
| @property auto ref back() | |
| { | |
| static if (primitivesToHandle & RangePrimitive.back) | |
| { | |
| try | |
| { | |
| return this.range.back; | |
| } | |
| catch (E exception) | |
| { | |
| return handler(exception, this.range); | |
| } | |
| } | |
| else | |
| return this.range.back; | |
| } | |
| void popBack() | |
| { | |
| static if (primitivesToHandle & RangePrimitive.popBack) | |
| { | |
| try | |
| { | |
| this.range.popBack(); | |
| } | |
| catch (E exception) | |
| { | |
| handler(exception, this.range); | |
| } | |
| } | |
| else | |
| this.range.popBack(); | |
| } | |
| } | |
| static if (isRandomAccessRange!Range) | |
| { | |
| auto ref opIndex(size_t index) | |
| { | |
| static if (primitivesToHandle & RangePrimitive.opIndex) | |
| { | |
| try | |
| { | |
| return this.range[index]; | |
| } | |
| catch (E exception) | |
| { | |
| static if (__traits(compiles, handler(exception, this.range, index))) | |
| return handler(exception, this.range, index); | |
| else | |
| return handler(exception, this.range); | |
| } | |
| } | |
| else | |
| return this.range[index]; | |
| } | |
| } | |
| static if (hasLength!Range) | |
| { | |
| @property auto length() | |
| { | |
| static if (primitivesToHandle & RangePrimitive.length) | |
| { | |
| try | |
| { | |
| return this.range.length; | |
| } | |
| catch (E exception) | |
| { | |
| return handler(exception, this.range); | |
| } | |
| } | |
| else | |
| return this.range.length; | |
| } | |
| } | |
| static if (hasSlicing!Range) | |
| { | |
| static if (hasLength!Range) | |
| { | |
| typeof(this) opSlice(size_t lower, size_t upper) | |
| { | |
| static if (primitivesToHandle & RangePrimitive.opSlice) | |
| { | |
| try | |
| { | |
| return typeof(this)(this.range[lower .. upper]); | |
| } | |
| catch (E exception) | |
| { | |
| return typeof(this)(handler(exception, this.range)); | |
| } | |
| } | |
| else | |
| return typeof(this)(this.range[lower .. upper]); | |
| } | |
| } | |
| else static if (is(typeof(Range.init[size_t.init .. $]))) | |
| { | |
| import std.range : Take, takeExactly; | |
| static struct DollarToken {} | |
| enum opDollar = DollarToken.init; | |
| typeof(this) opSlice(size_t lower, DollarToken) | |
| { | |
| static if (primitivesToHandle & RangePrimitive.opSlice) | |
| { | |
| try | |
| { | |
| return typeof(this)(this.range[lower .. $]); | |
| } | |
| catch (E exception) | |
| { | |
| return typeof(this)(handler(exception, this.range)); | |
| } | |
| } | |
| else | |
| return typeof(this)(this.range[lower .. $]); | |
| } | |
| Take!Handler opSlice(size_t lower, size_t upper) | |
| { | |
| static if (primitivesToHandle & RangePrimitive.opSlice) | |
| { | |
| try | |
| { | |
| return takeExactly(typeof(this)(this.range[lower .. $]), upper - 1); | |
| } | |
| catch (E exception) | |
| { | |
| return takeExactly(typeof(this)(handler(exception, this.range)), 0); | |
| } | |
| } | |
| else | |
| return takeExactly(typeof(this)(this.range[lower .. $]), upper - 1); | |
| } | |
| } | |
| } | |
| } | |
| return Handler(input); | |
| } | |
| /// | |
| pure @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.algorithm.iteration : map, splitter; | |
| import std.conv : to, ConvException; | |
| auto s = "12,1337z32,54,2,7,9,1z,6,8"; | |
| // The next line composition will throw when iterated | |
| // as some elements of the input do not convert to integer | |
| auto r = s.splitter(',').map!(a => to!int(a)); | |
| // Substitute 0 for cases of ConvException | |
| auto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0); | |
| assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8])); | |
| } | |
| /// | |
| pure @safe unittest | |
| { | |
| import std.algorithm.comparison : equal; | |
| import std.range : retro; | |
| import std.utf : UTFException; | |
| auto str = "hello\xFFworld"; // 0xFF is an invalid UTF-8 code unit | |
| auto handled = str.handle!(UTFException, RangePrimitive.access, | |
| (e, r) => ' '); // Replace invalid code points with spaces | |
| assert(handled.equal("hello world")); // `front` is handled, | |
| assert(handled.retro.equal("dlrow olleh")); // as well as `back` | |
| } | |
| pure nothrow @safe unittest | |
| { | |
| static struct ThrowingRange | |
| { | |
| pure @safe: | |
| @property bool empty() | |
| { | |
| throw new Exception("empty has thrown"); | |
| } | |
| @property int front() | |
| { | |
| throw new Exception("front has thrown"); | |
| } | |
| @property int back() | |
| { | |
| throw new Exception("back has thrown"); | |
| } | |
| void popFront() | |
| { | |
| throw new Exception("popFront has thrown"); | |
| } | |
| void popBack() | |
| { | |
| throw new Exception("popBack has thrown"); | |
| } | |
| int opIndex(size_t) | |
| { | |
| throw new Exception("opIndex has thrown"); | |
| } | |
| ThrowingRange opSlice(size_t, size_t) | |
| { | |
| throw new Exception("opSlice has thrown"); | |
| } | |
| @property size_t length() | |
| { | |
| throw new Exception("length has thrown"); | |
| } | |
| alias opDollar = length; | |
| @property ThrowingRange save() | |
| { | |
| throw new Exception("save has thrown"); | |
| } | |
| } | |
| static assert(isInputRange!ThrowingRange); | |
| static assert(isForwardRange!ThrowingRange); | |
| static assert(isBidirectionalRange!ThrowingRange); | |
| static assert(hasSlicing!ThrowingRange); | |
| static assert(hasLength!ThrowingRange); | |
| auto f = ThrowingRange(); | |
| auto fb = f.handle!(Exception, RangePrimitive.front | RangePrimitive.back, | |
| (e, r) => -1)(); | |
| assert(fb.front == -1); | |
| assert(fb.back == -1); | |
| assertThrown(fb.popFront()); | |
| assertThrown(fb.popBack()); | |
| assertThrown(fb.empty); | |
| assertThrown(fb.save); | |
| assertThrown(fb[0]); | |
| auto accessRange = f.handle!(Exception, RangePrimitive.access, | |
| (e, r) => -1); | |
| assert(accessRange.front == -1); | |
| assert(accessRange.back == -1); | |
| assert(accessRange[0] == -1); | |
| assertThrown(accessRange.popFront()); | |
| assertThrown(accessRange.popBack()); | |
| auto pfb = f.handle!(Exception, RangePrimitive.pop, (e, r) => -1)(); | |
| pfb.popFront(); // this would throw otherwise | |
| pfb.popBack(); // this would throw otherwise | |
| auto em = f.handle!(Exception, | |
| RangePrimitive.empty, (e, r) => false)(); | |
| assert(!em.empty); | |
| auto arr = f.handle!(Exception, | |
| RangePrimitive.opIndex, (e, r) => 1337)(); | |
| assert(arr[0] == 1337); | |
| auto arr2 = f.handle!(Exception, | |
| RangePrimitive.opIndex, (e, r, i) => i)(); | |
| assert(arr2[0] == 0); | |
| assert(arr2[1337] == 1337); | |
| auto save = f.handle!(Exception, | |
| RangePrimitive.save, | |
| function(Exception e, ref ThrowingRange r) { | |
| return ThrowingRange(); | |
| })(); | |
| save.save; | |
| auto slice = f.handle!(Exception, | |
| RangePrimitive.opSlice, (e, r) => ThrowingRange())(); | |
| auto sliced = slice[0 .. 1337]; // this would throw otherwise | |
| static struct Infinite | |
| { | |
| import std.range : Take; | |
| pure @safe: | |
| enum bool empty = false; | |
| int front() { assert(false); } | |
| void popFront() { assert(false); } | |
| Infinite save() @property { assert(false); } | |
| static struct DollarToken {} | |
| enum opDollar = DollarToken.init; | |
| Take!Infinite opSlice(size_t, size_t) { assert(false); } | |
| Infinite opSlice(size_t, DollarToken) | |
| { | |
| throw new Exception("opSlice has thrown"); | |
| } | |
| } | |
| static assert(isInputRange!Infinite); | |
| static assert(isInfinite!Infinite); | |
| static assert(hasSlicing!Infinite); | |
| assertThrown(Infinite()[0 .. $]); | |
| auto infinite = Infinite.init.handle!(Exception, | |
| RangePrimitive.opSlice, (e, r) => Infinite())(); | |
| auto infSlice = infinite[0 .. $]; // this would throw otherwise | |
| } | |
| /++ | |
| Convenience mixin for trivially sub-classing exceptions | |
| Even trivially sub-classing an exception involves writing boilerplate code | |
| for the constructor to: 1$(RPAREN) correctly pass in the source file and line number | |
| the exception was thrown from; 2$(RPAREN) be usable with $(LREF enforce) which | |
| expects exception constructors to take arguments in a fixed order. This | |
| mixin provides that boilerplate code. | |
| Note however that you need to mark the $(B mixin) line with at least a | |
| minimal (i.e. just $(B ///)) DDoc comment if you want the mixed-in | |
| constructors to be documented in the newly created Exception subclass. | |
| $(RED Current limitation): Due to | |
| $(LINK2 https://issues.dlang.org/show_bug.cgi?id=11500, bug #11500), | |
| currently the constructors specified in this mixin cannot be overloaded with | |
| any other custom constructors. Thus this mixin can currently only be used | |
| when no such custom constructors need to be explicitly specified. | |
| +/ | |
| mixin template basicExceptionCtors() | |
| { | |
| /++ | |
| Params: | |
| msg = The message for the exception. | |
| file = The file where the exception occurred. | |
| line = The line number where the exception occurred. | |
| next = The previous exception in the chain of exceptions, if any. | |
| +/ | |
| this(string msg, string file = __FILE__, size_t line = __LINE__, | |
| Throwable next = null) @nogc @safe pure nothrow | |
| { | |
| super(msg, file, line, next); | |
| } | |
| /++ | |
| Params: | |
| msg = The message for the exception. | |
| next = The previous exception in the chain of exceptions. | |
| file = The file where the exception occurred. | |
| line = The line number where the exception occurred. | |
| +/ | |
| this(string msg, Throwable next, string file = __FILE__, | |
| size_t line = __LINE__) @nogc @safe pure nothrow | |
| { | |
| super(msg, file, line, next); | |
| } | |
| } | |
| /// | |
| @safe unittest | |
| { | |
| class MeaCulpa: Exception | |
| { | |
| /// | |
| mixin basicExceptionCtors; | |
| } | |
| try | |
| throw new MeaCulpa("test"); | |
| catch (MeaCulpa e) | |
| { | |
| assert(e.msg == "test"); | |
| assert(e.file == __FILE__); | |
| assert(e.line == __LINE__ - 5); | |
| } | |
| } | |
| @safe pure nothrow unittest | |
| { | |
| class TestException : Exception { mixin basicExceptionCtors; } | |
| auto e = new Exception("msg"); | |
| auto te1 = new TestException("foo"); | |
| auto te2 = new TestException("foo", e); | |
| } | |
| @safe unittest | |
| { | |
| class TestException : Exception { mixin basicExceptionCtors; } | |
| auto e = new Exception("!!!"); | |
| auto te1 = new TestException("message", "file", 42, e); | |
| assert(te1.msg == "message"); | |
| assert(te1.file == "file"); | |
| assert(te1.line == 42); | |
| assert(te1.next is e); | |
| auto te2 = new TestException("message", e, "file", 42); | |
| assert(te2.msg == "message"); | |
| assert(te2.file == "file"); | |
| assert(te2.line == 42); | |
| assert(te2.next is e); | |
| auto te3 = new TestException("foo"); | |
| assert(te3.msg == "foo"); | |
| assert(te3.file == __FILE__); | |
| assert(te3.line == __LINE__ - 3); | |
| assert(te3.next is null); | |
| auto te4 = new TestException("foo", e); | |
| assert(te4.msg == "foo"); | |
| assert(te4.file == __FILE__); | |
| assert(te4.line == __LINE__ - 3); | |
| assert(te4.next is e); | |
| } |