1 /** 2 License: 3 Boost Software License - Version 1.0 - August 17th, 2003 4 5 Permission is hereby granted, free of charge, to any person or organization 6 obtaining a copy of the software and accompanying documentation covered by 7 this license (the "Software") to use, reproduce, display, distribute, 8 execute, and transmit the Software, and to prepare derivative works of the 9 Software, and to permit third-parties to whom the Software is furnished to 10 do so, all subject to the following: 11 12 The copyright notices in the Software and this entire statement, including 13 the above license grant, this restriction and the following disclaimer, 14 must be included in all copies of the Software, in whole or in part, and 15 all derivative works of the Software, unless such copies or derivative 16 works are solely in the form of machine-executable object code generated by 17 a source language processor. 18 19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 22 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 23 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 24 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 DEALINGS IN THE SOFTWARE. 26 27 Authors: 28 aermicioi 29 **/ 30 module aermicioi.aedi.storage.wrapper; 31 32 import std.traits; 33 import std.meta : staticMap; 34 35 @safe: 36 /** 37 Interface for components that wrap a component of type T. Provides boxing and automatic unboxing of wrapped values. 38 **/ 39 interface Wrapper(T) { 40 public { 41 42 /** 43 Get wrapped value out of wrapper. 44 45 Returns: 46 T the wrapped value 47 **/ 48 @property ref inout(T) value() inout return scope; 49 50 /** 51 Alias wrapper to T for automatic unboxing of values. 52 **/ 53 alias value this; 54 } 55 } 56 57 /** 58 Interface for components that denotes tha ability to be casted to a particular type T. 59 **/ 60 interface Castable(T) { 61 public { 62 63 /** 64 Get a representation of current component in type T. 65 66 Returns: 67 T component transformed into T component. 68 **/ 69 @property inout(T) casted() inout return scope; 70 71 /** 72 Alias casted type for automatic casting of component. 73 **/ 74 alias casted this; 75 } 76 } 77 78 /** 79 Wrapper over some component of type T. 80 81 Wraps up a value of type T, and aliases itself to it. This object is used 82 wrap any component that is not of reference type (class or interface), 83 in order to be saveable into an object storage. 84 Also thanks to alias value this semantics, in D is possible to do automatic 85 unboxing of values, just like Java does with simple values :P. 86 **/ 87 class WrapperImpl(T) : Wrapper!T { 88 mixin WrapperMixin!T; 89 } 90 91 /** 92 ditto 93 **/ 94 class CastableWrapperImpl(T, Castables...) : Wrapper!T, staticMap!(toCastable, Castables) { 95 mixin WrapperMixin!T; 96 97 mixin CastableMixin!(Castables); 98 } 99 100 /** 101 Downcast an object to component of T type. 102 103 This family of functions are aware of Wrapper!T and Castable!T interfaces, and 104 therefore will use them in downcasting process to T component. In case if T is 105 of class or interface type, the object is attempted to be downcast to T type first 106 if it fails, it will continue to cast to Wrapper!T and then Castable!T to find whether 107 component is downcastable to T type. If so, component of T type is returned, otherwise 108 an exception is thrown. For non referenced types, direct downcast is ignored and only 109 casting to Wrapper!T and Castable!T is attempted. In case if passed component is 110 an object implementing Wrapper!T or Castable!T, contents of those wrappers is returned. 111 For a component that does not implement those types, component itself will be returned. 112 113 Params: 114 T = the expected type of resolved component. 115 component = component to be downcasted. 116 117 Throws: 118 InvalidCastException when resolved component is not of expected type. 119 120 Returns: 121 T referenced object 122 **/ 123 @trusted T unwrap(T)(inout(Object) component) 124 if (is(T == class) || is(T == interface)) { 125 import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException; 126 127 { 128 T result = cast(T) component; 129 130 if (result !is null) { 131 return result; 132 } 133 } 134 135 { 136 Wrapper!T result = cast(Wrapper!T) component; 137 138 if (result !is null) { 139 return result.value; 140 } 141 } 142 143 { 144 Castable!T result = cast(Castable!T) component; 145 146 if (result !is null) { 147 148 return result.casted; 149 } 150 } 151 152 throw new InvalidCastException("Could not downcast component of ${actual} type, expected ${expected} type", null, typeid(T), component.classinfo); 153 } 154 155 /** 156 ditto 157 **/ 158 @trusted T unwrap(T)(inout(Object) component) 159 if (!(is(T == class) || is(T == interface))) { 160 import aermicioi.aedi.exception.invalid_cast_exception : InvalidCastException; 161 162 { 163 Wrapper!T result = cast(Wrapper!T) component; 164 165 if (result !is null) { 166 return result.value; 167 } 168 } 169 170 { 171 Castable!T result = cast(Castable!T) component; 172 173 if (result !is null) { 174 175 return result.casted; 176 } 177 } 178 179 throw new InvalidCastException("Could not downcast component of ${actual} type, expected ${expected} type", null, typeid(T), component.classinfo); 180 } 181 182 /** 183 ditto 184 **/ 185 @safe Z unwrap(T : Castable!Z, Z)(T castable) { 186 return castable.casted; 187 } 188 189 /** 190 ditto 191 **/ 192 @safe Z unwrap(T : Wrapper!Z, Z)(T wrapper) { 193 194 return wrapper.value; 195 } 196 197 /** 198 ditto 199 **/ 200 @safe T unwrap(T)(auto ref T wrapped) { 201 return wrapped; 202 } 203 204 private { 205 206 mixin template WrapperMixin(T) { 207 private { 208 T value_; 209 } 210 211 public { 212 213 this() @disable; 214 215 this(ref T value) { 216 this.value_ = value; 217 } 218 219 this(T value) { 220 this.value_ = value; 221 } 222 223 @property ref inout(T) value() inout return scope { 224 return this.value_; 225 } 226 } 227 } 228 229 mixin template CastableMixin() { 230 231 } 232 233 mixin template CastableMixin(Type) { 234 @property { 235 inout(Type) casted() inout return scope { 236 return cast(inout(Type)) this.value; 237 } 238 } 239 } 240 241 mixin template CastableMixin(Type, Second, More...) { 242 243 mixin CastableMixin!(Type); 244 245 mixin CastableMixin!(Second); 246 247 mixin CastableMixin!(More); 248 } 249 250 alias toCastable(T) = Castable!T; 251 }