1 /** 2 This module implements utility code to throw exceptions in @nogc code. 3 */ 4 module nogc.exception; 5 6 7 import std.experimental.allocator.mallocator: Mallocator; 8 9 10 T enforce(E = NoGcException, T, Args...) 11 (T value, auto ref Args args, in string file = __FILE__, in size_t line = __LINE__) 12 if (is(typeof({ if (!value) {} }))) 13 { 14 import std.functional: forward; 15 if(!value) throw new NoGcException(NoGcException.Dummy(), file, line, forward!args); 16 return value; 17 } 18 19 alias NoGcException = NoGcExceptionImpl!Mallocator; 20 21 class NoGcExceptionImpl(A): Exception { 22 23 import automem.traits: isGlobal; 24 import automem.vector: StringA; 25 import std.meta: anySatisfy; 26 27 alias Allocator = A; 28 29 // just to let enforce pass the right arguments to the constructor 30 protected static struct Dummy {} 31 protected enum isDummy(T) = is(T == Dummy); 32 33 private StringA!Allocator _msg; 34 static if(!isGlobal!Allocator) private Allocator _allocator; 35 36 this(Args...) 37 (auto ref Args args, string file = __FILE__, size_t line = __LINE__) 38 if(isGlobal!Allocator && !anySatisfy!(isDummy, Args)) 39 { 40 import std.functional: forward; 41 this(Dummy(), file, line, forward!args); 42 } 43 44 /// 45 @("exception can be constructed in @nogc code") 46 @safe @nogc pure unittest { 47 static const exception = new NoGcException(); 48 } 49 50 this(Args...) 51 (Allocator allocator, auto ref Args args, string file = __FILE__, size_t line = __LINE__) 52 if(!anySatisfy!(isDummy, Args)) 53 { 54 import std.functional: forward; 55 this(Dummy(), file, line, forward!args); 56 this._allocator = allocator; 57 } 58 59 // exists to be used from throw_ 60 protected this(Args...) 61 (in Dummy _, in string file, in size_t line, scope auto ref Args args) 62 if(isGlobal!Allocator) 63 { 64 import nogc.conv: text, BUFFER_SIZE; 65 import std.functional: forward; 66 67 _msg = text!(BUFFER_SIZE, A)(forward!args); 68 // We don't ever append to the vector so it will never invalidate the slice. 69 // Changing _msg to be `const` doesn't compile though. 70 super(() @trusted { return _msg[]; }(), file, line); 71 } 72 73 /** 74 Throws a new NoGcException allowing to adjust the file name and line number 75 */ 76 static void throw_(T = typeof(this), Args...)(in File file, in Line line, scope auto ref Args args) { 77 import std.functional: forward; 78 throw new T(Dummy(), file.value, line.value, forward!args); 79 } 80 81 /// Because DIP1008 doesn't do what it should yet 82 final void free() @safe @nogc scope { 83 _msg.free; 84 } 85 } 86 87 struct File { string value; } 88 struct Line { size_t value; } 89 90 91 mixin template NoGcExceptionCtors() { 92 93 import nogc.exception: File, Line; 94 import automem.traits: isGlobal; 95 import std.meta: anySatisfy; 96 97 this(Args...) 98 (auto ref Args args, string file = __FILE__, size_t line = __LINE__) 99 if(isGlobal!Allocator && !anySatisfy!(isDummy, Args)) 100 { 101 import std.functional: forward; 102 super(forward!args, file, line); 103 } 104 105 this(Args...) 106 (Allocator allocator, auto ref Args args, string file = __FILE__, size_t line = __LINE__) 107 if(!anySatisfy!(isDummy, Args)) 108 { 109 import std.functional: forward; 110 super(allocator, forward!args, file, line); 111 } 112 113 114 // exists to be used from throw_ 115 protected this(Args...) 116 (in Dummy _, in string file, in size_t line, scope auto ref Args args) 117 if(isGlobal!Allocator) 118 { 119 import std.functional: forward; 120 super(_, file, line, forward!args); 121 } 122 123 /** 124 Throws a new NoGcException allowing to adjust the file name and line number 125 */ 126 static void throw_(T = typeof(this), Args...)(in File file, in Line line, scope auto ref Args args) { 127 import std.functional: forward; 128 typeof(super).throw_!T(file, line, forward!args); 129 } 130 }