1 module notnull; 2 3 /** Thrown if $(D nnL) gets passed a pointer that is null. 4 */ 5 class NullPointerException : Exception { 6 @safe pure nothrow this(string file = __FILE__, 7 size_t line = __LINE__, Throwable next = null) 8 { 9 super("Tried to deference a null pointer", file, line, next ); 10 } 11 12 @safe pure nothrow this(NullPointerException old) { 13 super(old.msg, old.file, old.line, old.next); 14 } 15 } 16 17 /** Returns a reference to the object the passed pointer points to. 18 If $(D ptr) is null a $(D NullPointerException) is thrown. 19 */ 20 ref T nnL(T)(return scope T* ptr, string file = __FILE__, 21 size_t line = __LINE__) 22 { 23 if(ptr is null) { 24 throw new NullPointerException(file, line); 25 } 26 27 return *ptr; 28 } 29 30 unittest { 31 int a; 32 nnL(&a); 33 34 (&a).nnL(); 35 } 36 37 unittest { 38 import std.exception : assertThrown; 39 int* a = null; 40 assertThrown!NullPointerException(nnL(a)); 41 } 42 43 unittest { 44 import std.exception : assertThrown, assertNotThrown; 45 struct A { 46 } 47 48 struct B { 49 A* a; 50 } 51 52 B* b; 53 assertThrown!NullPointerException(nnL(b)); 54 55 b = new B; 56 assertNotThrown(nnL(b)); 57 58 assertThrown!NullPointerException(nnL(b).a.nnL()); 59 assertThrown!NullPointerException(b.nnL().a.nnL()); 60 61 b.a = new A; 62 assertNotThrown(nnL(b).a.nnL()); 63 } 64 65 private string buildErrorStringRecur(E...)() if(E.length > 0) { 66 return E[0].stringof ~ "," ~ buildErrorStringRecur!(E[1 .. $]); 67 } 68 69 private string buildErrorStringRecur(E...)() if(E.length == 0) { 70 return "}"; 71 } 72 73 private string buildErrorString(Exp...)() { 74 return "enum ErrorType { hasData, hasNoData, " ~ 75 buildErrorStringRecur!Exp(); 76 } 77 78 private string buildThrowSwitch(Exp...)() { 79 return `switch(this.error) { 80 case ErrorType.hasData: 81 goto default; 82 default: 83 break; 84 case ErrorType.hasNoData: 85 assert(false, "No Data nor an Exception Present"); 86 `~ buildThrowSwitchRecu!(Exp)(); 87 } 88 89 private string buildThrowSwitchRecu(E...)() if(E.length > 0) { 90 static if(is(E[0] == Exception)) { 91 return "case ErrorType.Exception:" ~ 92 "throw new Exception(" ~ 93 "(cast(Exception)(this.payload.ptr)).msg," ~ 94 "(cast(Exception)(this.payload.ptr)).file," ~ 95 "(cast(Exception)(this.payload.ptr)).line," ~ 96 "(cast(Exception)(this.payload.ptr)).next);" 97 ~ buildThrowSwitchRecu!(E[1 .. $]); 98 } else static if(is(E[0] == Error)) { 99 return "case ErrorType.Error:" ~ 100 "throw new Error(" ~ 101 "(cast(Error)(this.payload.ptr)).msg," ~ 102 "(cast(Error)(this.payload.ptr)).file," ~ 103 "(cast(Error)(this.payload.ptr)).line," ~ 104 "(cast(Error)(this.payload.ptr)).next);" 105 ~ buildThrowSwitchRecu!(E[1 .. $]); 106 } else static if(is(E[0] == Throwable)) { 107 return "case ErrorType.Throwable:" ~ 108 "throw new Throwable(" ~ 109 "(cast(Throwable)(this.payload.ptr)).msg," ~ 110 "(cast(Throwable)(this.payload.ptr)).file," ~ 111 "(cast(Throwable)(this.payload.ptr)).line," ~ 112 "(cast(Throwable)(this.payload.ptr)).next);" 113 ~ buildThrowSwitchRecu!(E[1 .. $]); 114 } else { 115 return "case ErrorType." ~ E[0].stringof ~ `: 116 throw new ` ~ E[0].stringof ~ "((cast(" ~ E[0].stringof 117 ~ ")(this.payload.ptr)));" 118 ~ buildThrowSwitchRecu!(E[1 .. $]); 119 } 120 } 121 122 private string buildThrowSwitchRecu(E...)() if(E.length == 0) { 123 return "}"; 124 } 125 126 private template classSize(C) if(is(C : Exception)) { 127 enum size_t classSize = __traits(classInstanceSize, C); 128 } 129 130 struct Null(T,Exp...) { 131 import std.algorithm.comparison : max; 132 import std.meta : staticMap; 133 import std.traits : isImplicitlyConvertible; 134 135 alias Type = T; 136 137 static if(Exp.length > 0) { 138 align(8) 139 void[max(staticMap!(classSize, Exp), T.sizeof)] payload; 140 } else { 141 align(8) 142 void[T.sizeof] payload; 143 } 144 mixin(buildErrorString!Exp()); 145 ErrorType error = ErrorType.hasNoData; 146 147 @property bool isNull() pure nothrow @nogc const { 148 return this.error >= ErrorType.hasNoData; 149 } 150 151 @property bool isNotNull() pure nothrow @nogc const { 152 return this.error == ErrorType.hasData; 153 } 154 155 void opAssign(S)(auto ref S s) if(isImplicitlyConvertible!(S,T)) { 156 *(cast(T*)(this.payload.ptr)) = s; 157 this.error = ErrorType.hasData; 158 } 159 160 void set(E, string file = __FILE__, size_t line = __LINE__, Args...)(auto ref Args args) { 161 import std.conv : emplace; 162 emplace!E(this.payload[0 .. $], args, file, line); 163 mixin("this.error = ErrorType." ~ E.stringof ~ ";"); 164 } 165 166 ErrorType getError() const pure @safe nothrow @nogc { 167 return this.error; 168 } 169 170 ref T get() @trusted { 171 if(this.error == ErrorType.hasData) { 172 return *(cast(T*)(this.payload.ptr)); 173 } 174 mixin(buildThrowSwitch!(Exp)()); 175 assert(false); 176 } 177 178 void rethrow() { 179 mixin(buildThrowSwitch!(Exp)()); 180 } 181 } 182 183 T makeNull(T,S)(auto ref S s) { 184 T ret; 185 ret = s; 186 return ret; 187 } 188 189 T makeNull(T,E, string file = __FILE__, size_t line = __LINE__, Args...) 190 (auto ref Args args) 191 { 192 T ret; 193 ret.set!(E,file,line)(args); 194 return ret; 195 } 196 197 pure unittest { 198 alias NullInt = Null!(int, NullPointerException, Exception); 199 NullInt ni; 200 pragma(msg, NullInt.sizeof); 201 assert(ni.isNull); 202 assert(!ni.isNotNull); 203 204 ni = 10; 205 assert(ni.get() == 10); 206 } 207 208 pure unittest { 209 alias NullInt = Null!(int, NullPointerException, Exception); 210 NullInt ni; 211 212 string f = __FILE__; 213 int l = __LINE__ + 1; 214 ni.set!NullPointerException(); 215 assert(ni.getError() == NullInt.ErrorType.NullPointerException); 216 217 NullPointerException npe; 218 try { 219 ni.rethrow(); 220 } catch(NullPointerException e) { 221 npe = e; 222 } 223 assert(npe !is null); 224 assert(npe.file == f, npe.file); 225 assert(npe.line == l); 226 } 227 228 pure unittest { 229 alias NullInt = Null!(int, NullPointerException, Exception); 230 auto ni = makeNull!NullInt(10); 231 assert(ni.isNotNull); 232 assert(ni.get() == 10); 233 } 234 235 pure @safe unittest { 236 alias NullT = Null!(float); 237 NullT nf; 238 } 239 240 pure unittest { 241 alias NullInt = Null!(int, NullPointerException, Exception); 242 auto ni = makeNull!(NullInt,NullPointerException)(); 243 assert(ni.isNull); 244 } 245 246 pure unittest { 247 alias NullInt = Null!(int, NullPointerException, Exception); 248 string msg = "some message"; 249 auto ni = makeNull!(NullInt,Exception)(msg); 250 assert(ni.isNull); 251 252 try { 253 ni.rethrow(); 254 } catch(Exception e) { 255 assert(msg == e.msg); 256 return; 257 } 258 assert(false, "Wrong Exception rethrown"); 259 } 260 261 pure unittest { 262 alias NullInt = Null!(int, NullPointerException, Exception); 263 NullInt ni; 264 assert(ni.isNull); 265 assert(!ni.isNotNull); 266 267 ni = 10; 268 assert(!ni.isNull); 269 assert(ni.isNotNull); 270 assert(ni.get() == 10); 271 272 ni.set!NullPointerException(); 273 assert(ni.isNull); 274 }