1 /** 2 Taken from Phobos, tweaked for convenience 3 4 Copyright: Copyright the respective authors, 2008- 5 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 6 Authors: $(WEB erdani.org, Andrei Alexandrescu), 7 $(WEB bartoszmilewski.wordpress.com, Bartosz Milewski), 8 Don Clugston, 9 Shin Fujishiro, 10 Kenji Hara 11 */ 12 13 module memutils.unique; 14 15 import memutils.allocators; 16 import memutils.constants; 17 import memutils.utils; 18 import std.conv : to; 19 20 enum DebugUnique = true; 21 22 // TODO: Move release() into Embed!, and add a releaseCheck() for refCounted (cannot release > 1 reference) 23 struct Unique(T, ALLOC = void) 24 { 25 /** Represents a reference to $(D T). Resolves to $(D T*) if $(D T) is a value type. */ 26 alias TR = RefTypeOf!T; 27 28 public: 29 /** 30 Constructor that takes an rvalue. 31 It will ensure uniqueness, as long as the rvalue 32 isn't just a view on an lvalue (e.g., a cast). 33 Typical usage: 34 ---- 35 Unique!Foo f = new Foo; 36 ---- 37 */ 38 this(inout TR p) 39 { 40 opAssign(cast(TR)p); 41 } 42 /** 43 Constructor that takes an lvalue. It nulls its source. 44 The nulling will ensure uniqueness as long as there 45 are no previous aliases to the source. 46 */ 47 this(ref TR p) 48 { 49 opAssign(p); 50 } 51 52 /** 53 Constructor that takes a $(D Unique) of a type that is convertible to our type. 54 55 Typically used to transfer a $(D Unique) rvalue of derived type to 56 a $(D Unique) of base type. 57 Example: 58 --- 59 class C : Object {} 60 61 Unique!C uc = new C; 62 Unique!Object uo = uc.release; 63 --- 64 */ 65 this(U)(Unique!U u) 66 if (is(u.TR:TR)) 67 { 68 // logTrace("Unique constructor converting from ", U.stringof); 69 opAssign(u._p); 70 u._p = null; 71 } 72 73 void free() 74 { 75 TR p = null; 76 opAssign(p); 77 } 78 79 void opAssign()(auto ref TR p) 80 { 81 if (_p) destroy(this); 82 if (!p) return; 83 //logTrace("Unique ctor of ", T.stringof, " : ", ptr.to!string); 84 static if (HasDebugAllocations && DebugUnique) { 85 ptree._defaultInitialize(); 86 if(cast(void*)p in ptree) 87 { 88 logDebug("Already owned pointer: " ~ p.to!string ~ " of type " ~ T.stringof); 89 assert(false); 90 } 91 ptree.insert(cast(void*)p); 92 } 93 _p = p; 94 p = null; 95 } 96 /* 97 void opAssign(U)(in Unique!U p) 98 { 99 debug(Unique) logTrace("Unique opAssign converting from ", U.stringof); 100 // first delete any resource we own 101 destroy(this); 102 _p = cast(TR)u._p; 103 cast(TR)u._p = null; 104 }*/ 105 106 /// Transfer ownership from a $(D Unique) of a type that is convertible to our type. 107 void opAssign(U)(Unique!U u) 108 if (is(u.TR:TR)) 109 { 110 opAssign(u._p); 111 u._p = null; 112 } 113 114 ~this() 115 { 116 //logDebug("Unique destructor of ", T.stringof, " : ", ptr); 117 import std.c.string : memset; 118 119 120 static if (ALLOC.stringof != "void") { 121 if (_p) { 122 //logTrace("ptr in ptree: ", ptr in ptree); 123 124 static if (HasDebugAllocations && DebugUnique) { 125 ptree._defaultInitialize(); 126 assert(ptr in ptree); 127 ptree.remove(ptr); 128 } 129 130 ObjectAllocator!(T, ALLOC).free(_p); 131 132 //static if (HasDebugAllocations && DebugUnique) 133 // debug memset(ptr, 0, AllocSize!T); 134 } 135 } 136 else { 137 if (_p) { 138 //logTrace("ptr in ptree: ", ptr in ptree); 139 140 static if (HasDebugAllocations && DebugUnique) { 141 ptree._defaultInitialize(); 142 if (ptr !in ptree){ 143 logDebug("Unknown pointer: " ~ ptr.to!string ~ " of type " ~ T.stringof); 144 assert(false); 145 } 146 ptree.remove(ptr); 147 } 148 149 static if (is(TR == T*)) .destroy(*_p); 150 else .destroy(_p); 151 //delete _p; 152 153 //static if (HasDebugAllocations && DebugUnique) { 154 // debug memset(ptr, 0, AllocSize!T); 155 //} 156 } 157 } 158 159 _p = null; 160 } 161 /** Returns whether the resource exists. */ 162 @property bool isEmpty() const 163 { 164 return _p is null; 165 } 166 167 /** Transfer ownership to a $(D Unique) rvalue. Nullifies the current contents. */ 168 TR release() 169 { 170 //logTrace("Release"); 171 if (!_p) return null; 172 auto ret = _p; 173 drop(); 174 return ret; 175 } 176 177 void drop() 178 { 179 //logTrace("Drop"); 180 if (!_p) return; 181 static if (HasDebugAllocations && DebugUnique) { 182 ptree._defaultInitialize(); 183 assert(ptr in ptree); 184 ptree.remove(ptr); 185 } 186 _p = null; 187 } 188 189 /** Forwards member access to contents. */ 190 TR opDot() { return _p; } 191 const(TR) opDot() const { return _p; } 192 193 TR opUnary(string op)() if (op == "*") { return _p; } 194 const(TR) opUnary(string op)() const if (op == "*") { return _p; } 195 196 TR get() { return _p; } 197 198 bool opCast(T : bool)() const { 199 return !isEmpty; 200 } 201 202 /** 203 Postblit operator is undefined to prevent the cloning of $(D Unique) objects. 204 */ 205 @disable this(this); 206 207 private: 208 TR _p; 209 210 @property void* ptr() const { 211 return cast(void*)_p; 212 } 213 214 static if (HasDebugAllocations && DebugUnique) { 215 import memutils.rbtree; 216 static RBTree!(void*, "a < b", true, Malloc) ptree; 217 } 218 } 219 220 auto unique(T)(T obj) { 221 return Unique!T(obj); 222 }