1 /++ 2 `@nogc` exceptions and errors definitions. 3 +/ 4 module mir.exception; 5 6 /++ 7 +/ 8 class MirException : Exception 9 { 10 /// 11 mixin MirThrowableImpl; 12 } 13 14 /// Generic style 15 @safe pure nothrow @nogc 16 unittest 17 { 18 import mir.exception; 19 try throw new MirException("Hi D", 2, "!"); 20 catch(Exception e) assert(e.msg == "Hi D2!"); 21 } 22 23 /// C++ style 24 @safe pure nothrow @nogc 25 unittest 26 { 27 import mir.exception; 28 import mir.format; 29 try throw new MirException(stringBuf() << "Hi D" << 2 << "!" << getData); 30 catch(Exception e) assert(e.msg == "Hi D2!"); 31 } 32 33 /// Low-level style 34 @safe pure nothrow @nogc 35 unittest 36 { 37 import mir.exception; 38 import mir.format; 39 stringBuf buf; 40 try throw new MirException(buf.print( "Hi D", 2, "!").data); 41 catch(Exception e) assert(e.msg == "Hi D2!"); 42 } 43 44 /// 45 @safe pure nothrow @nogc 46 unittest 47 { 48 @safe pure nothrow @nogc 49 bool func(scope const(char)[] msg) 50 { 51 /// scope messages are copied 52 try throw new MirException(msg); 53 catch(Exception e) assert(e.msg == msg); 54 55 /// immutable strings are not copied 56 static immutable char[] gmsg = "global msg"; 57 try throw new MirException(gmsg); 58 catch(Exception e) assert(e.msg is gmsg); 59 60 return __ctfe; 61 } 62 63 assert(func("runtime-time check") == 0); 64 65 static assert(func("compile-time check") == 1); 66 } 67 68 // /// 69 // auto ref enforce(T, Args...)(scope auto return ref T arg, lazy @nogc Args args, string file = __FILE__, int line = __LINE__) @nogc 70 // if (Args.length) 71 // { 72 // import mir.utility: _expect; 73 // static if (__traits(compiles, arg !is null)) 74 // { 75 // if (_expect(arg !is null, true)) 76 // return arg; 77 // } 78 // else 79 // { 80 // if (_expect(cast(bool)arg, true)) 81 // return arg; 82 // } 83 // import mir.format; 84 // stringBuf buf; 85 // throw new MirException(buf.print(args).data, file, line); 86 // } 87 88 // /// 89 // @safe pure nothrow @nogc 90 // unittest 91 // { 92 // import mir.exception; 93 // try enforce(false, "Hi D", 2, "!"); 94 // catch(Exception e) assert(e.msg == "Hi D2!"); 95 // } 96 97 // /// 98 // auto ref enforce(T)(scope auto return ref T arg, lazy scope const(char)[] msg, string file = __FILE__, int line = __LINE__) @nogc 99 // { 100 // import mir.functional: forward; 101 // import mir.utility: _expect; 102 // static if (__traits(compiles, arg !is null)) 103 // { 104 // if (_expect(arg !is null, true)) 105 // return forward!arg[0]; 106 // } 107 // else 108 // { 109 // if (_expect(cast(bool)arg, true)) 110 // return forward!arg[0]; 111 // } 112 // throw new MirException(msg, file, line); 113 // } 114 115 // /// 116 // @safe pure nothrow @nogc 117 // unittest 118 // { 119 // import mir.exception; 120 // try enforce(false, "Msg"); 121 // catch(Exception e) assert(e.msg == "Msg"); 122 // } 123 124 /// 125 auto ref enforce(string fmt, string file = __FILE__, int line = __LINE__, Expr)(scope auto return ref Expr arg) @trusted 126 { 127 import mir.functional: forward; 128 import mir.utility: _expect; 129 static if (__traits(compiles, arg !is null)) 130 { 131 if (_expect(arg !is null, true)) 132 return forward!arg[0]; 133 } 134 else 135 { 136 if (_expect(cast(bool)arg, true)) 137 return forward!arg[0]; 138 } 139 static immutable exception = new Exception(fmt, file, line); 140 throw exception; 141 } 142 143 /// 144 @safe pure nothrow @nogc 145 unittest 146 { 147 import mir.exception; 148 try enforce!"Msg"(false); 149 catch(Exception e) assert(e.msg == "Msg"); 150 } 151 152 /++ 153 +/ 154 class MirError : Error 155 { 156 /// 157 mixin MirThrowableImpl; 158 } 159 160 /// 161 @system pure nothrow @nogc 162 unittest 163 { 164 @system pure nothrow @nogc 165 bool func(scope const(char)[] msg) 166 { 167 /// scope messages are copied 168 try throw new MirException(msg); 169 catch(Exception e) assert(e.msg == msg); 170 171 /// immutable strings are not copied 172 static immutable char[] gmsg = "global msg"; 173 try throw new MirError(gmsg); 174 catch(Error e) assert(e.msg is gmsg); 175 176 return __ctfe; 177 } 178 179 assert(func("runtime-time check") == 0); 180 181 static assert(func("compile-time check") == 1); 182 } 183 184 /++ 185 +/ 186 mixin template MirThrowableImpl() 187 { 188 private bool _global; 189 private char[maxMsgLen] _payload = void; 190 191 /++ 192 Params: 193 msg = message. No-scope `msg` is assumed to have the same lifetime as the throwable. scope strings are copied to internal buffer. 194 file = file name, zero terminated global string 195 line = line number 196 nextInChain = next exception in the chain (optional) 197 +/ 198 @nogc @safe pure nothrow this(scope const(char)[] msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) 199 { 200 super((() @trusted => cast(immutable) initilizePayload(_payload, msg))(), file, line, nextInChain); 201 } 202 203 /// ditto 204 @nogc @safe pure nothrow this(scope const(char)[] msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) 205 { 206 this(msg, file, line, nextInChain); 207 } 208 209 /// ditto 210 @nogc @safe pure nothrow this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) 211 { 212 this._global = true; 213 super(msg, file, line, nextInChain); 214 } 215 216 /// ditto 217 @nogc @safe pure nothrow this(string msg, Throwable nextInChain, string file = __FILE__, size_t line = __LINE__) 218 { 219 this._global = true; 220 super(msg, file, line, nextInChain); 221 } 222 223 /// 224 ~this() @trusted 225 { 226 import mir.internal.memory: free; 227 if (!_global && msg.ptr != _payload.ptr) 228 free(cast(void*)msg.ptr); 229 } 230 231 /++ 232 Generic multiargument overload. 233 Constructs a string using the `print` function. 234 +/ 235 @nogc @safe pure nothrow this(Args...)(scope auto ref Args args, string file = __FILE__, size_t line = __LINE__, Throwable nextInChain = null) 236 if (Args.length > 1) 237 { 238 import mir.format; 239 stringBuf buf; 240 this(buf.print(args).data, file, line, nextInChain); 241 } 242 } 243 244 private enum maxMsgLen = 447; 245 246 pragma(inline, false) 247 pure nothrow @nogc @safe 248 private const(char)[] initilizePayload(ref return char[maxMsgLen] payload, scope const(char)[] msg) 249 { 250 import mir.internal.memory: malloc; 251 import core.stdc.string: memcpy; 252 if (msg.length > payload.length) 253 { 254 if (auto ret = (() @trusted 255 { 256 if (__ctfe) 257 return null; 258 if (auto ptr = malloc(msg.length)) 259 { 260 memcpy(ptr, msg.ptr, msg.length); 261 return cast(const(char)[]) ptr[0 .. msg.length]; 262 } 263 return null; 264 })()) 265 return ret; 266 msg = msg[0 .. payload.length]; 267 // remove tail UTF-8 symbol chunk if any 268 uint c = msg[$-1]; 269 if (c > 0b_0111_1111) 270 { 271 do { 272 c = msg[$-1]; 273 msg = msg[0 .. $ - 1]; 274 } 275 while (msg.length && c < 0b_1100_0000); 276 } 277 } 278 if (__ctfe) 279 payload[][0 .. msg.length] = msg; 280 else 281 (() @trusted => memcpy(payload.ptr, msg.ptr, msg.length))(); 282 return payload[0 .. msg.length]; 283 }