Sourcestd/exception.d
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 assertint a = 3;enforce(a > 2,"a needs to be higher than 2.");// enforce can throw a custom exceptionenforce!ConvException(a > 2,"a needs to be higher than 2.");// enforce will return it's inputenum size = 42;auto memory = enforce(malloc(size),"malloc failed")[0 .. size];scope(exit) free(memory.ptr);// collectException can be used to test for exceptionsException e = collectException("abc".to!int);assert(e.file.endsWith("conv.d"));// and just for the exception messagestring msg = collectExceptionMsg("abc".to!int);writeln(msg);// "Unexpected 'a' when converting from type string to type int"// assertThrown can be used to assert that an exception is thrownassertThrown!ConvException("abc".to!int);// ifThrown can be used to provide a default value if an exception is thrownwriteln("x".to!int().ifThrown(0));// 0// handle is a more advanced version of ifThrown for rangesauto 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 exceptionsstaticclass MeaCulpa : Exception{mixin basicExceptionCtors;}e = collectException((){thrownew MeaCulpa("diagnostic message");}());writeln(e.msg);// "diagnostic message"writeln(e.file);// __FILE__writeln(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 carechar[] str =" mutable".dup;str[0 .. 2] ="im";immutable res = assumeUnique(str);writeln(res);// "immutable"
assertNotThrown(T : Throwable = Exception, E)(lazy Eexpression, stringmsg = null, stringfile = __FILE__, size_tline = __LINE__);| T | TheThrowable to test for. |
Eexpression | The expression to test. |
stringmsg | 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. |
stringfile | The file where the error occurred. Defaults to__FILE__. |
size_tline | The line where the error occurred. Defaults to__LINE__. |
expression.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!`);
assertThrown(T : Throwable = Exception, E)(lazy Eexpression, stringmsg = null, stringfile = __FILE__, size_tline = __LINE__);| T | TheThrowable to test for. |
Eexpression | The expression to test. |
stringmsg | Optional message to output on test failure. |
stringfile | The file where the error occurred. Defaults to__FILE__. |
size_tline | The line where the error occurred. Defaults to__LINE__. |
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.`);
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))enforce(T, Dg, string file = __FILE__, size_t line = __LINE__)(Tvalue, scope Dgdg)dg())) && is(typeof((){if (!value){}})));enforce(T)(Tvalue, lazy Throwableex);dg - custom delegate that return a string and is only called if an exception occurredex - custom exception to be thrown. It islazy and is only created if an exception occurredTvalue | The value to test. |
| E | Exception type to throw if the value evaluates to false. |
| const(char)[] msg | The error message to put in the exception if it is thrown. |
Dgdg | The delegate to be called if the value evaluates to false. |
Throwableex | The exception to throw if the value evaluates to false. |
| string file | The source file of the caller. |
| size_t line | The line number of the caller. |
value, ifcast(bool)value is true. Otherwise, depending on the chosen overload,new Exception(msg),dg() orex is thrown.enforce is used to throw exceptions and is therefore intended to aid in error handling. It isnot intended for verifying the logic of your program - that is whatassert is for.enforce inside of contracts (i.e. inside ofin andout blocks andinvariants), because contracts are compiled out when compiling with-release.import core.stdc.stdlib : malloc, free;import std.conv : ConvException, to;// use enforce like assertint a = 3;enforce(a > 2,"a needs to be higher than 2.");// enforce can throw a custom exceptionenforce!ConvException(a > 2,"a needs to be higher than 2.");// enforce will return it's inputenum size = 42;auto memory =enforce(malloc(size),"malloc failed")[0 .. size];scope(exit) free(memory.ptr);
assertNotThrown(enforce(true,new Exception("this should not be thrown")));assertThrown(enforce(false,new Exception("this should be thrown")));
writeln(enforce(123));// 123try{enforce(false,"error");assert(false);}catch (Exception e){ writeln(e.msg);// "error" writeln(e.file);// __FILE__ writeln(e.line);// __LINE__ - 7}
import std.conv : ConvException;alias convEnforce =enforce!ConvException;assertNotThrown(convEnforce(true));assertThrown!ConvException(convEnforce(false,"blah"));
enforce(T)(Tvalue, lazy const(char)[]msg = null, stringfile = __FILE__, size_tline = __LINE__)value){}})));errnoEnforce = enforce!(ErrnoException).enforce(T)(T value, lazy const(char)[] msg = null, string file = __FILE__, size_t line = __LINE__) if (is(typeof((){if (!value){}})));| T value | The value to test. |
| const(char)[] msg | The message to include in theErrnoException if it is thrown. |
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 !isnull);// expect a non-empty line
collectException(T = Exception, E)(lazy Eexpression, ref Eresult);result is set to the result of the expression.collectExceptioncan be used to collect anyThrowable and not justExceptions, it is generally ill-advised to catch anything that is neither anException nor a type derived fromException. So, do not usecollectException to collect non-Exceptions unless you're sure that that's what you really want to do.| T | The type of exception to catch. |
Eexpression | The expression which may throw an exception. |
Eresult | The result of the expression if no exception is thrown. |
int b;int foo() {thrownew Exception("blah"); }assert(collectException(foo(), b));version (D_NoBoundsChecks) {}else{// check for out of bounds errorint[] a =newint[3];import core.exception : RangeError;assert(collectException!RangeError(a[4], b));}
collectException(T : Throwable = Exception, E)(lazy Eexpression);collectExceptioncan be used to collect anyThrowable and not justExceptions, it is generally ill-advised to catch anything that is neither anException nor a type derived fromException. So, do not usecollectException to collect non-Exceptions unless you're sure that that's what you really want to do.| T | The type of exception to catch. |
Eexpression | The expression which may throw an exception. |
int foo() {thrownew Exception("blah"); }writeln(collectException(foo()).msg);// "blah"
collectExceptionMsg(T = Exception, E)(lazy Eexpression);collectExceptionMsgcan be used to collect anyThrowable and not justExceptions, it is generally ill-advised to catch anything that is neither anException nor a type derived fromException. So, do not usecollectExceptionMsg to collect non-Exceptions unless you're sure that that's what you really want to do.| T | The type of exception to catch. |
Eexpression | The expression which may throw an exception. |
void throwFunc() {thrownew Exception("My Message."); }writeln(collectExceptionMsg(throwFunc()));// "My Message."void nothrowFunc() {}assert(collectExceptionMsg(nothrowFunc())isnull);void throwEmptyFunc() {thrownew Exception(""); }writeln(collectExceptionMsg(throwEmptyFunc()));// emptyExceptionMsg
emptyExceptionMsg;assumeUnique(T)(T[]array);assumeUnique(T)(ref T[]array);assumeUnique(T, U)(ref T[U]array);assumeUnique just inserts a cast,but its name documents assumptions on the part of thecaller.assumeUnique(arr) should only be called whenthere are no more active mutable aliases to elements ofarr. To strengthen this assumption,assumeUnique(arr)also clearsarr before returning. EssentiallyassumeUnique(arr) indicates commitment from the caller that thereis no more mutable access to any ofarr's elements(transitively), and that all future accesses will be done throughthe immutable array returned byassumeUnique.assumeUnique is used to return arrays fromfunctions that have allocated and built them.T[]array | The array to cast to immutable. |
Example
string letters(){char[] result =newchar['z' - 'a' + 1];foreach (i,ref e; result) { e =cast(char)('a' + i); }returnassumeUnique(result);}assumeUnique.Bad
char[] buffer;string letters(char first,char last){if (first >= last)returnnull;// fineauto sneaky = buffer; sneaky.length = last - first + 1;foreach (i,ref e; sneaky) { e =cast(char)('a' + i); }returnassumeUnique(sneaky);// BAD}
return to!(string)(sneaky);// not that sneaky anymoreTheto call will duplicate the array appropriately.
assumeUnique because the compiler can infer theuniqueness of the array in the pure function:static string letters()pure{char[] result =newchar['z' - 'a' + 1];foreach (i,ref e; result) { e =cast(char)('a' + i); }return result;}
assumeUnique'sconvention-based usage is that at this time there is noformal checking of the correctness of the assumption;on the upside, the idiomatic use ofassumeUnique issimple and rare enough to be tolerable.int[] arr =newint[1];auto arr1 = arr.assumeUnique;staticassert(is(typeof(arr1) ==immutable(int)[]));writeln(arr);// nullwriteln(arr1);// [0]
int[string] arr = ["a":1];auto arr1 = arr.assumeUnique;staticassert(is(typeof(arr1) ==immutable(int[string])));writeln(arr);// nullwriteln(arr1.keys);// ["a"]
assumeWontThrow(T)(lazy Texpr, stringmsg = null, stringfile = __FILE__, size_tline = __LINE__);expr. If it turns out that the expressiondoes throw at runtime, the wrapper will throw anAssertError. (Note thatThrowable objects such asAssertError that do not subclassException may be thrown even fromnothrow functions, since they are considered to be serious runtime problems that cannot be recovered from.)Texpr | The expression asserted not to throw. |
stringmsg | The message to include in theAssertError if the assumption turns out to be false. |
stringfile | The source file name of the caller. |
size_tline | The line number of the caller. |
expr, if any.import std.math.algebraic : sqrt;// This function may throw.int squareRoot(int x){if (x < 0)thrownew Exception("Tried to take root of negative number");returncast(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.returnassumeWontThrow(squareRoot(x*x + y*y));}writeln(computeLength(3, 4));// 5
doesPointTo(S, T, Tdummy = void)(auto ref const Ssource, ref const Ttarget)source) || isDynamicArray!S || is(S == U*, U) || is(S == class));doesPointTo(S, T)(auto ref const shared Ssource, ref const shared Ttarget);mayPointTo(S, T, Tdummy = void)(auto ref const Ssource, ref const Ttarget)source) || isDynamicArray!S || is(S == U*, U) || is(S == class));mayPointTo(S, T)(auto ref const shared Ssource, ref const shared Ttarget);Ssource | The source object |
Ttarget | The target object |
source's representation embeds a pointerthat points totarget's representation or somewhere insideit.Ifsource is or contains a dynamic array, then, then these functions will checkif there is overlap between the dynamic array andtarget's representation.Ifsource is a class, then it will be handled as a pointer.Iftarget is a pointer, a dynamic array or a class, then these functions will onlycheck ifsource points totarget,not whattarget references.Ifsource is or contains a union orvoid[n], then there may be either false positives orfalse negatives:doesPointTo will returntrue if it is absolutely certainsource points totarget. It may produce false negatives, but neverfalse positives. This function should be prefered when trying to validateinput data.mayPointTo will returnfalse if it is absolutely certainsource does not point totarget. It may produce false positives, but neverfalse negatives. This function should be prefered for defensively choosing acode path.NoteEvaluatingdoesPointTo(x, x) checks whetherx hasinternal 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).
int i = 0;int* p =null;assert(!p.doesPointTo(i));p = &i;assert( p.doesPointTo(i));
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
int i;// trick the compiler when initializing slice// https://issues.dlang.org/show_bug.cgi?id=18637int* 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));
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
ErrnoException:object.Exception;import core.stdc.errno : EAGAIN;auto ex =newErrnoException("oh no", EAGAIN);writeln(ex.errno);// EAGAIN
import core.stdc.errno : errno, EAGAIN;auto old = errno;scope(exit) errno = old;// fake that errno got set by the calleeerrno = EAGAIN;auto ex =newErrnoException("oh no");writeln(ex.errno);// EAGAIN
errno() scope;errnoMsg() scope;msg, stringfile = null, size_tline = 0);msg, interrno, stringfile = null, size_tline = 0);ifThrown(E : Throwable = Exception, T1, T2)(lazy scope T1expression, lazy scope T2errorHandler);ifThrown(E : Throwable, T1, T2)(lazy scope T1expression, scope T2 delegate(E)errorHandler);ifThrown(T1, T2)(lazy scope T1expression, scope T2 delegate(Exception)errorHandler);| E | The type ofThrowables to catch. Defaults toException |
| T1 | The type of the expression. |
| T2 | The return type of the error handler. |
T1expression | The expression to run and return its result. |
T2errorHandler | The handler to run if the expression throwed. |
import std.conv : to;writeln("x".to!int.ifThrown(0));// 0
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 errorsalias orFallback = (lazy a) => a.ifThrown!ConvException("not a number") .ifThrown!Exception("number too small");writeln(orFallback(enforce("x".to!int < 1).to!string));// "not a number"writeln(orFallback(enforce("2".to!int < 1).to!string));// "number too small"
// null and new Object have a common type(Object).staticassert(is(typeof(null.ifThrown(new Object())) == Object));staticassert(is(typeof((new Object()).ifThrown(null)) == Object));// 1 and new Object do not have a common type.staticassert(!__traits(compiles, 1.ifThrown(new Object())));staticassert(!__traits(compiles, (new Object()).ifThrown(1)));
import std.format : format;writeln("%s".format.ifThrown!Exception(e =>typeid (e).name));// "std.format.FormatException"
RangePrimitive: int;RangePrimitive.access is a shortcut for the access primitives;front,back andopIndex.RangePrimitive.pop is a shortcut for the mutating primitives;popFront andpopBack.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 integerauto r = s.splitter(',').map!(a => to!int(a));// Substitute 0 for cases of ConvExceptionauto h = r.handle!(ConvException,RangePrimitive.front, (e, r) => 0);assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8]));
import std.algorithm.comparison : equal;import std.range : retro;import std.utf : UTFException;auto str ="hello\xFFworld";// 0xFF is an invalid UTF-8 code unitauto handled = str.handle!(UTFException,RangePrimitive.access, (e, r) => ' ');// Replace invalid code points with spacesassert(handled.equal("hello world"));// `front` is handled,assert(handled.retro.equal("dlrow olleh"));// as well as `back`
frontbackpopFrontpopBackemptysavelengthopDollaropIndexopSliceaccesspophandle(E : Throwable, RangePrimitive primitivesToHandle, alias handler, Range)(Rangeinput)| E | The type ofThrowable to handle. |
| primitivesToHandle | Set of range primitives to handle. |
| handler | The callable that is called when a handled primitive throws aThrowable of typeE. The handler must accept arguments of the formE, ref IRange and its return value is used as the primitive's return value wheneverE is thrown. ForopIndex, the handler can optionally recieve a third argument; the index that caused the exception. |
Rangeinput | The range to handle. |
input.NoteInfinite ranges with slicing support must return an instance ofstd.range.Take when sliced with a specific lower and upperbound (seestd.range.primitives.hasSlicing);handle deals withthis bytakeing 0 from the return value of the handler function andreturning that when an exception is caught.
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 integerauto r = s.splitter(',').map!(a => to!int(a));// Substitute 0 for cases of ConvExceptionauto h = r.handle!(ConvException, RangePrimitive.front, (e, r) => 0);assert(h.equal([12, 0, 54, 2, 7, 9, 0, 6, 8]));
import std.algorithm.comparison : equal;import std.range : retro;import std.utf : UTFException;auto str ="hello\xFFworld";// 0xFF is an invalid UTF-8 code unitauto handled = str.handle!(UTFException, RangePrimitive.access, (e, r) => ' ');// Replace invalid code points with spacesassert(handled.equal("hello world"));// `front` is handled,assert(handled.retro.equal("dlrow olleh"));// as well as `back`
basicExceptionCtors()class MeaCulpa: Exception{///mixinbasicExceptionCtors;}trythrownew MeaCulpa("test");catch (MeaCulpa e){ writeln(e.msg);// "test" writeln(e.file);// __FILE__ writeln(e.line);// __LINE__ - 5}
msg, stringfile = __FILE__, size_tline = __LINE__, Throwablenext = null);stringmsg | The message for the exception. |
stringfile | The file where the exception occurred. |
size_tline | The line number where the exception occurred. |
Throwablenext | The previous exception in the chain of exceptions, if any. |
msg, Throwablenext, stringfile = __FILE__, size_tline = __LINE__);stringmsg | The message for the exception. |
Throwablenext | The previous exception in the chain of exceptions. |
stringfile | The file where the exception occurred. |
size_tline | The line number where the exception occurred. |