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