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 super(_msg[], file, line); 69 } 70 71 /** 72 Throws a new NoGcException allowing to adjust the file name and line number 73 */ 74 static void throw_(T = typeof(this), Args...)(in File file, in Line line, scope auto ref Args args) { 75 import std.functional: forward; 76 throw new T(Dummy(), file.value, line.value, forward!args); 77 } 78 79 /// Because DIP1008 doesn't do what it should yet 80 final void free() @safe @nogc scope { 81 _msg.free; 82 } 83 } 84 85 struct File { string value; } 86 struct Line { size_t value; } 87 88 89 mixin template NoGcExceptionCtors() { 90 91 import nogc.exception: File, Line; 92 import automem.traits: isGlobal; 93 import std.meta: anySatisfy; 94 95 this(Args...) 96 (auto ref Args args, string file = __FILE__, size_t line = __LINE__) 97 if(isGlobal!Allocator && !anySatisfy!(isDummy, Args)) 98 { 99 import std.functional: forward; 100 super(forward!args, file, line); 101 } 102 103 this(Args...) 104 (Allocator allocator, auto ref Args args, string file = __FILE__, size_t line = __LINE__) 105 if(!anySatisfy!(isDummy, Args)) 106 { 107 import std.functional: forward; 108 super(allocator, forward!args, file, line); 109 } 110 111 112 // exists to be used from throw_ 113 protected this(Args...) 114 (in Dummy _, in string file, in size_t line, scope auto ref Args args) 115 if(isGlobal!Allocator) 116 { 117 import std.functional: forward; 118 super(_, file, line, forward!args); 119 } 120 121 /** 122 Throws a new NoGcException allowing to adjust the file name and line number 123 */ 124 static void throw_(T = typeof(this), Args...)(in File file, in Line line, scope auto ref Args args) { 125 import std.functional: forward; 126 typeof(super).throw_!T(file, line, forward!args); 127 } 128 }