1 /* 2 Copyright (c) 2019 Victor Porton, 3 Pure Dependency Injector - http://freesoft.portonvictor.org 4 5 This file is part of Pure Dependency Injector. 6 7 Pure Dependency Injector is free software: you can redistribute it and/or modify 8 it under the terms of the GNU Affero General Public License as 9 published by the Free Software Foundation, either version 3 of the 10 License, or (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU Affero General Public License for more details. 16 17 You should have received a copy of the GNU Affero General Public License 18 along with this program. If not, see <http://www.gnu.org/licenses/>. 19 */ 20 21 module pure_dependency.providers; 22 23 import std.typecons; 24 import std.traits; 25 import memoize; 26 import struct_params; 27 28 /** 29 Base class for non-reference providers. 30 31 * `Result_` the type of the object to be provided. 32 33 * `Params_` the type of provider parameters. 34 */ 35 class Provider(Result_, Params_...) { 36 alias Result = Result_; /// the type of the object to be provided. 37 alias Params = Params_; /// the type of provider parameters. 38 /// Call the provider. 39 final Result opCall(Params params) { 40 return delegate_(params); 41 } 42 /// Call it with a structure or class as the argument (expanding its members into arguments 43 /// in order). 44 final Result call(S)(S s) { 45 return callMemberFunctionWithParamsStruct!(this, "opCall", S)(s); 46 } 47 /// The abstract virtual function used to create the provided object. 48 abstract Result delegate_(Params params); 49 alias DelegateType = Result delegate (Params params); 50 /// Returns `delegate_` as a delegate. 51 final @property DelegateType provider() { 52 return &delegate_; 53 } 54 } 55 56 /** 57 Base class for reference providers. 58 59 * `Result_` the type of the object to be provided. 60 61 * `Params_` the type of provider parameters. 62 */ 63 class ReferenceProvider(Result_, Params_...) { 64 alias Result = Result_; /// the type of the object to be provided. 65 alias Params = Params_; /// the type of provider parameters. 66 /// Call the provider. 67 final ref Result opCall(Params params) { 68 return delegate_(params); 69 } 70 /// Call it with a structure or class as the argument (expanding its members into arguments 71 /// in order). 72 final ref Result call(S)(S s) const { 73 return callMemberFunctionWithParamsStruct!(this, "opCall", S)(s); 74 } 75 /// The abstract virtual function used to create the provided object. 76 abstract ref Result delegate_(Params params); 77 alias DelegateType = ref Result delegate (Params params); 78 /// Returns `delegate_` as a delegate. 79 final @property DelegateType provider() { 80 return &delegate_; 81 } 82 } 83 84 //class ClassFactory(Result, Params...) : Provider!(Result, Params) { 85 // override ref Result delegate_(Params params) { 86 // return new Result(params); 87 // } 88 //} 89 // 90 //class StructFactory(Result, Params...) : Provider!(Result, Params) { 91 // override ref Result delegate_(Params params) { 92 // return Result(params); 93 // } 94 //} 95 96 /** 97 Non-reference provider that calls some function to create the provided object. 98 99 `Function` the function called to create the provided object. 100 */ 101 class Callable(alias Function) : Provider!(ReturnType!Function, Parameters!Function) { 102 override ReturnType!Function delegate_(Params params) { 103 return Function(params); 104 } 105 } 106 107 /** 108 Reference provider that calls some function to create the provided object. 109 110 `Function` the function called to create the provided object. 111 */ 112 class ReferenceCallable(alias Function) : ReferenceProvider!(ReturnType!Function, Parameters!Function) { 113 override ref ReturnType!Function delegate_(Params params) { 114 return Function(params); 115 } 116 } 117 118 /** 119 Base class for non-reference singletons. Uses another provider to create the object. 120 121 `Base` is the type of this another provider. 122 */ 123 class BaseGeneralSingleton(Base) : Provider!(Base.Result, Base.Params) { 124 private Base _base; 125 /// Create the singleton with given base provider. 126 this(Base base) { 127 _base = base; 128 } 129 /// Get the base provider. 130 @property ref Base base() { return _base; } 131 } 132 133 /** 134 Base class for reference singletons. Uses another provider to create the object. 135 136 `Base` is the type of this another provider. 137 */ 138 class ReferenceBaseGeneralSingleton(Base) : ReferenceProvider!(Base.Result, Base.Params) { 139 private Base _base; 140 /// Create the singleton with given base provider. 141 this(Base base) { 142 _base = base; 143 } 144 /// Get the base provider. 145 @property ref Base base() { return _base; } 146 } 147 148 /** 149 Non-reference thread-unsafe singleton. 150 */ 151 class ThreadUnsafeSingleton(Base) : BaseGeneralSingleton!Base { 152 this(Base base) { super(base); } 153 override Result delegate_(Params params) { 154 return noLockMemoizeMember!(Base, "delegate_")(base, params); 155 } 156 } 157 158 /** 159 Reference thread-unsafe singleton. 160 */ 161 class ReferenceThreadUnsafeSingleton(Base) : ReferenceBaseGeneralSingleton!Base { 162 this(Base base) { super(base); } 163 override ref Result delegate_(Params params) { 164 return referenceNoLockMemoizeMember!(Base, "delegate_")(base, params); 165 } 166 } 167 168 /** 169 Non-reference thread-safe singleton. 170 */ 171 class ThreadSafeSingleton(Base) : BaseGeneralSingleton!Base { 172 this(Base base) { super(base); } 173 override Result delegate_(Params params) { 174 return synchronizedMemoizeMember!(Base, "delegate_")(base, params); 175 } 176 } 177 178 /** 179 Reference thread-safe singleton. 180 */ 181 class ReferenceThreadSafeSingleton(Base) : ReferenceBaseGeneralSingleton!Base { 182 this(Base base) { super(base); } 183 override ref Result delegate_(Params params) { 184 return referenceSynchronizedMemoizeMember!(Base, "delegate_")(base, params); 185 } 186 } 187 188 /** 189 Non-reference thread-local singleton. 190 */ 191 class ThreadLocalSingleton(Base) : BaseGeneralSingleton!Base { 192 this(Base base) { super(base); } 193 override Result delegate_(Params params) { 194 synchronized { 195 return memoizeMember!(Base, "delegate_")(base, params); 196 } 197 } 198 } 199 200 /** 201 Reference thread-local singleton. 202 */ 203 class ReferenceThreadLocalSingleton(Base) : ReferenceBaseGeneralSingleton!Base { 204 this(Base base) { super(base); } 205 override ref Result delegate_(Params params) { 206 synchronized { 207 return referenceMemoizeMember!(Base, "delegate_")(base, params); 208 } 209 } 210 } 211 212 /** 213 Non-refernce provider that always returns the same object. 214 215 `obj` is the object returned by the provider. 216 */ 217 class FixedObject(alias obj) : Provider!(typeof(obj)) { 218 override Result delegate_() const { 219 return obj; 220 } 221 } 222 223 /** 224 Refernce provider that always returns the same object. 225 226 `obj` is the object returned by the provider. 227 */ 228 class ReferenceFixedObject(alias obj) : ReferenceProvider!(typeof(obj)) { 229 override ref Result delegate_() const { 230 return obj; 231 } 232 } 233 234 // TODO: *CallableSingleton classes are of an inefficient implementation (holding a pointer). 235 236 /** 237 Non-reference singleton provider that calls some function to create the provided object. 238 239 `Function` the function called to create the provided object. 240 */ 241 class ThreadUnsafeCallableSingleton(alias Function) : ThreadUnsafeSingleton!(Callable!Function) { 242 this() { 243 super(new Callable!Function); 244 } 245 } 246 247 /** 248 Reference singleton provider that calls some function to create the provided object. 249 250 `Function` the function called to create the provided object. 251 */ 252 class ReferenceThreadUnsafeCallableSingleton(alias Function) : ReferenceThreadUnsafeSingleton!(ReferenceCallable!Function) { 253 this() { 254 super(new ReferenceCallable!Function); 255 } 256 } 257 258 /** 259 Non-reference singleton provider that calls some function to create the provided object. 260 261 `Function` the function called to create the provided object. 262 */ 263 class ThreadSafeCallableSingleton(alias Function) : ThreadSafeSingleton!(Callable!Function) { 264 this() { 265 super(new Callable!Function); 266 } 267 } 268 269 /** 270 Reference singleton provider that calls some function to create the provided object. 271 272 `Function` the function called to create the provided object. 273 */ 274 class ReferenceThreadSafeCallableSingleton(alias Function) : ReferenceThreadSafeSingleton!(ReferenceCallable!Function) { 275 this() { 276 super(new ReferenceCallable!Function); 277 } 278 } 279 280 /** 281 Non-reference singleton provider that calls some function to create the provided object. 282 283 `Function` the function called to create the provided object. 284 */ 285 class ThreadLocalCallableSingleton(alias Function) : ThreadLocalSingleton!(Callable!Function) { 286 this() { 287 super(new Callable!Function); 288 } 289 } 290 291 /** 292 Reference singleton provider that calls some function to create the provided object. 293 294 `Function` the function called to create the provided object. 295 */ 296 class ReferenceThreadLocalCallableSingleton(alias Function) : ReferenceThreadLocalSingleton!(ReferenceCallable!Function) { 297 this() { 298 super(new ReferenceCallable!Function); 299 } 300 } 301 302 /** 303 Create provider with `defaults` of type `ParamsType.Regular` from a provider `Base`. 304 305 `ParamsType` should be like a struct `S` created with 306 ```d 307 mixin StructParams!("S", int, "x", float, "y"); // see struct-params package 308 ``` 309 310 Then use it like this: 311 ```d 312 float calc(int x, float y) { 313 return x * y; 314 } 315 immutable S.WithDefaults myDefaults = { x: 3, y: 2.1 }; // or `immutable S.Regular myDefaults` 316 alias MyProvider = ProviderWithDefaults!(Callable!calc, S, myDefaults); 317 318 immutable S.WithDefaults providerParams = { x: 2 }; // note y is default initialized to null 319 auto provider = new MyProvider; 320 assert(provider.callWithDefaults(providerParams) - 4.2 < 1e-6); 321 ``` 322 */ 323 class ProviderWithDefaults(Base, ParamsType, alias defaults) : Base { 324 Result callWithDefaults(ParamsType.WithDefaults params) { 325 return call(combine(params, defaults)); 326 } 327 } 328 329 unittest { 330 class C { 331 int v; 332 this(int a, int b) { v = a + b; } 333 } 334 auto cFactory = new Callable!((int a, int b) => new C(a, b)); 335 assert(cFactory(1, 2).v == 3); 336 } 337 338 unittest { 339 int x; 340 ref int f() { return x; } 341 auto cFactory = new ReferenceCallable!(f); 342 assert(&cFactory() == &x); 343 } 344 345 unittest { 346 int obj = 3; 347 int f(int a, int b) { return a + b; } 348 ref int g() { return obj; } 349 350 assert(new FixedObject!obj()() == obj); 351 assert(&new ReferenceFixedObject!obj()() == &obj); 352 353 assert(new ThreadUnsafeCallableSingleton!f()(2, 5) == 7); 354 auto refSingleton = new ReferenceThreadUnsafeCallableSingleton!g(); 355 assert(refSingleton() == refSingleton()); 356 357 assert(new ThreadSafeCallableSingleton!f()(2, 5) == 7); 358 auto refSingleton2 = new ReferenceThreadSafeCallableSingleton!g(); 359 assert(refSingleton2() == refSingleton2()); 360 361 assert(new ThreadLocalCallableSingleton!f()(2, 5) == 7); 362 auto refSingleton3 = new ReferenceThreadLocalCallableSingleton!g(); 363 assert(refSingleton3() == refSingleton3()); 364 } 365 366 unittest { 367 mixin StructParams!("S", int, "x", float, "y"); 368 float calc(int x, float y) { 369 return x * y; 370 } 371 immutable S.Regular myDefaults = { x: 3, y: 2.1 }; 372 alias MyProvider = ProviderWithDefaults!(Callable!calc, S, myDefaults); 373 immutable S.WithDefaults myDefaults2 = { x: 3, y: 2.1 }; 374 alias MyProvider2 = ProviderWithDefaults!(Callable!calc, S, myDefaults2); 375 376 immutable S.WithDefaults providerParams = { x: 2 }; // note y is default initialized to null 377 auto provider = new MyProvider; 378 auto provider2 = new MyProvider2; 379 import std.math : abs; 380 assert((provider.callWithDefaults(providerParams) - 4.2).abs < 1e-6); 381 assert((provider.callWithDefaults(providerParams) - 4.2).abs < 1e-6); 382 } 383