1 /** 2 * Basic reference-counting for classes. 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <vladimir@thecybershadow.net> 12 */ 13 14 module ae.utils.meta.rcclass; 15 16 import core.memory; 17 18 import std.conv : emplace; 19 20 private struct RCClassStore(C) 21 { 22 size_t refCount = void; 23 void[__traits(classInstanceSize, C)] data = void; 24 } 25 26 struct RCClass(C) 27 if (is(C == class)) 28 { 29 // storage 30 31 private RCClassStore!C* _rcClassStore; 32 33 @property C _rcClassGet() 34 { 35 return cast(C)_rcClassStore.data.ptr; 36 } 37 38 alias _rcClassGet this; 39 40 // construction 41 42 this(T)(T value) 43 if (is(T == RCClass!U, U) && is(typeof({U u; C c = u;}))) 44 { 45 _rcClassStore = cast(RCClassStore!C*)value._rcClassStore; 46 if (_rcClassStore) 47 _rcClassStore.refCount++; 48 } 49 50 // operations 51 52 ref typeof(this) opAssign(T)(T value) 53 if (is(T == typeof(null))) 54 { 55 _rcClassDestroy(); 56 _rcClassStore = null; 57 return this; 58 } 59 60 ref typeof(this) opAssign(T)(auto ref T value) 61 if (is(T == RCClass!U, U) && is(typeof({U u; C c = u;}))) 62 { 63 _rcClassDestroy(); 64 _rcClassStore = cast(RCClassStore!C*)value._rcClassStore; 65 if (_rcClassStore) 66 _rcClassStore.refCount++; 67 return this; 68 } 69 70 T opCast(T)() 71 if (is(T == RCClass!U, U) && is(typeof({C c; U u = c;}))) 72 { 73 T result; 74 result._rcClassStore = cast(typeof(result._rcClassStore))_rcClassStore; 75 if (_rcClassStore) 76 _rcClassStore.refCount++; 77 return result; 78 } 79 80 bool opCast(T)() 81 if (is(T == bool)) 82 { 83 return !!_rcClassStore; 84 } 85 86 auto opCall(Args...)(auto ref Args args) 87 if (is(typeof(_rcClassGet.opCall(args)))) 88 { 89 return _rcClassGet.opCall(args); 90 } 91 92 // lifetime 93 94 void _rcClassDestroy() 95 { 96 if (_rcClassStore && --_rcClassStore.refCount == 0) 97 { 98 static if (__traits(hasMember, C, "__xdtor")) 99 _rcClassGet.__xdtor(); 100 GC.free(_rcClassStore); 101 } 102 } 103 104 this(this) 105 { 106 _rcClassStore.refCount++; 107 } 108 109 ~this() 110 { 111 _rcClassDestroy(); 112 } 113 } 114 115 // Use external factory function instead of static opCall to avoid 116 // conflicting with class's non-static opCall 117 118 template rcClass(C) 119 if (is(C == class)) 120 { 121 RCClass!C rcClass(Args...)(auto ref Args args) 122 if (is(C == class) && is(typeof(emplace!C(null, args)))) 123 { 124 RCClass!C c; 125 c._rcClassStore = new RCClassStore!C; 126 c._rcClassStore.refCount = 1; 127 emplace!C(c._rcClassStore.data[], args); 128 return c; 129 } 130 } 131 132 /// Constructors 133 unittest 134 { 135 void ctorTest(bool haveArglessCtor, bool haveArgCtor)() 136 { 137 static class C 138 { 139 int n = -1; 140 141 static if (haveArglessCtor) 142 this() { n = 1; } 143 144 static if (haveArgCtor) 145 this(int val) { n = val; } 146 147 ~this() { n = -2; } 148 } 149 150 RCClass!C rc; 151 assert(!rc); 152 153 static if (haveArglessCtor || !haveArgCtor) 154 { 155 rc = rcClass!C(); 156 assert(rc); 157 static if (haveArglessCtor) 158 assert(rc.n == 1); 159 else 160 assert(rc.n == -1); // default value 161 } 162 else 163 static assert(!is(typeof(rcClass!C()))); 164 165 static if (haveArgCtor) 166 { 167 rc = rcClass!C(42); 168 assert(rc); 169 assert(rc.n == 42); 170 } 171 else 172 static assert(!is(typeof(rcClass!C(1)))); 173 174 rc = null; 175 assert(!rc); 176 } 177 178 import std.meta : AliasSeq; 179 foreach (haveArglessCtor; AliasSeq!(false, true)) 180 foreach (haveArgCtor; AliasSeq!(false, true)) 181 ctorTest!(haveArglessCtor, haveArgCtor); 182 } 183 184 /// Lifetime 185 unittest 186 { 187 static class C 188 { 189 static int counter; 190 191 this() { counter++; } 192 ~this() { counter--; } 193 } 194 195 { 196 auto a = rcClass!C(); 197 assert(C.counter == 1); 198 auto b = a; 199 assert(C.counter == 1); 200 } 201 assert(C.counter == 0); 202 } 203 204 /// Inheritance 205 unittest 206 { 207 static class Base 208 { 209 int foo() { return 1; } 210 } 211 212 static class Derived : Base 213 { 214 override int foo() { return 2; } 215 } 216 217 auto derived = rcClass!Derived(); 218 RCClass!Base base = derived; // initialization 219 base = derived; // assignment 220 static assert(!is(typeof(derived = base))); 221 auto base2 = cast(RCClass!Base)derived; 222 } 223 224 /// Non-static opCall 225 unittest 226 { 227 static class C 228 { 229 int calls; 230 void opCall() { calls++; } 231 } 232 233 auto c = rcClass!C(); 234 assert(c.calls == 0); 235 c(); 236 assert(c.calls == 1); 237 }