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 // operations 41 42 ref typeof(this) opAssign(T)(T value) 43 if (is(T == typeof(null))) 44 { 45 _rcClassDestroy(); 46 _rcClassStore = null; 47 return this; 48 } 49 50 ref typeof(this) opAssign(T)(auto ref T value) 51 if (is(T == RCClass!U, U) && is(typeof({U u; C c = u;}))) 52 { 53 _rcClassDestroy(); 54 _rcClassStore = cast(RCClassStore!C*)value._rcClassStore; 55 if (_rcClassStore) 56 _rcClassStore.refCount++; 57 return this; 58 } 59 60 T opCast(T)() 61 if (is(T == RCClass!U, U) && is(typeof({C c; U u = c;}))) 62 { 63 T result; 64 result._rcClassStore = cast(typeof(result._rcClassStore))_rcClassStore; 65 if (_rcClassStore) 66 _rcClassStore.refCount++; 67 return result; 68 } 69 70 bool opCast(T)() 71 if (is(T == bool)) 72 { 73 return !!_rcClassStore; 74 } 75 76 auto opCall(Args...)(auto ref Args args) 77 if (is(typeof(_rcClassGet.opCall(args)))) 78 { 79 return _rcClassGet.opCall(args); 80 } 81 82 // lifetime 83 84 void _rcClassDestroy() 85 { 86 if (_rcClassStore && --_rcClassStore.refCount == 0) 87 { 88 static if (__traits(hasMember, C, "__xdtor")) 89 _rcClassGet.__xdtor(); 90 GC.free(_rcClassStore); 91 } 92 } 93 94 this(this) 95 { 96 _rcClassStore.refCount++; 97 } 98 99 ~this() 100 { 101 _rcClassDestroy(); 102 } 103 } 104 105 // Use external factory function instead of static opCall to avoid 106 // conflicting with class's non-static opCall 107 108 template rcClass(C) 109 if (is(C == class)) 110 { 111 RCClass!C rcClass(Args...)(auto ref Args args) 112 if (is(C == class) && is(typeof(emplace!C(null, args)))) 113 { 114 RCClass!C c; 115 c._rcClassStore = new RCClassStore!C; 116 c._rcClassStore.refCount = 1; 117 emplace!C(c._rcClassStore.data[], args); 118 return c; 119 } 120 } 121 122 /// Constructors 123 unittest 124 { 125 void ctorTest(bool haveArglessCtor, bool haveArgCtor)() 126 { 127 static class C 128 { 129 int n = -1; 130 131 static if (haveArglessCtor) 132 this() { n = 1; } 133 134 static if (haveArgCtor) 135 this(int val) { n = val; } 136 137 ~this() { n = -2; } 138 } 139 140 RCClass!C rc; 141 assert(!rc); 142 143 static if (haveArglessCtor || !haveArgCtor) 144 { 145 rc = rcClass!C(); 146 assert(rc); 147 static if (haveArglessCtor) 148 assert(rc.n == 1); 149 else 150 assert(rc.n == -1); // default value 151 } 152 else 153 static assert(!is(typeof(rcClass!C()))); 154 155 static if (haveArgCtor) 156 { 157 rc = rcClass!C(42); 158 assert(rc); 159 assert(rc.n == 42); 160 } 161 else 162 static assert(!is(typeof(rcClass!C(1)))); 163 164 rc = null; 165 assert(!rc); 166 } 167 168 import std.meta : AliasSeq; 169 foreach (haveArglessCtor; AliasSeq!(false, true)) 170 foreach (haveArgCtor; AliasSeq!(false, true)) 171 ctorTest!(haveArglessCtor, haveArgCtor); 172 } 173 174 /// Lifetime 175 unittest 176 { 177 static class C 178 { 179 static int counter; 180 181 this() { counter++; } 182 ~this() { counter--; } 183 } 184 185 { 186 auto a = rcClass!C(); 187 assert(C.counter == 1); 188 auto b = a; 189 assert(C.counter == 1); 190 } 191 assert(C.counter == 0); 192 } 193 194 /// Inheritance 195 unittest 196 { 197 static class Base 198 { 199 int foo() { return 1; } 200 } 201 202 static class Derived : Base 203 { 204 override int foo() { return 2; } 205 } 206 207 auto derived = rcClass!Derived(); 208 RCClass!Base base; 209 base = derived; 210 static assert(!is(typeof(derived = base))); 211 auto base2 = cast(RCClass!Base)derived; 212 } 213 214 /// Non-static opCall 215 unittest 216 { 217 static class C 218 { 219 int calls; 220 void opCall() { calls++; } 221 } 222 223 auto c = rcClass!C(); 224 assert(c.calls == 0); 225 c(); 226 assert(c.calls == 1); 227 }