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     Alexandru Ermicioi
29 **/
30 module aermicioi.aedi_property_reader.core.placeholder;
31 
32 import aermicioi.aedi : Wrapper;
33 import aermicioi.aedi_property_reader.core.traits : n;
34 import std.experimental.allocator;
35 import std.conv;
36 
37 /**
38 Interface for objects that are aware of a type and can provide it.
39 **/
40 interface TypeAware {
41 
42     /**
43     Get the type that is stored in component.
44 
45     Returns:
46         TypeInfo stored in component.
47     **/
48     TypeInfo type() const nothrow @property @nogc;
49 }
50 
51 /**
52 Interface for objects that can hold a value in.
53 **/
54 interface Placeholder(T) : TypeAware {
55 
56     @property {
57 
58         /**
59         Get stored value.
60 
61         Returns:
62             T value stored in component
63         **/
64         ref inout(T) value() nothrow @safe inout @nogc;
65 
66         /**
67         Store value in component.
68 
69         Params:
70             value = value that will be stored in.
71 
72         Returns:
73             Reference to stored value.
74         **/
75         ref T value(ref T value) nothrow @safe;
76 
77         /**
78         ditto
79         **/
80         final ref T value(T value) nothrow @safe {
81             return this.value(value);
82         }
83 
84         /**
85         Subtyping to stored value.
86         **/
87         alias value this;
88     }
89 }
90 
91 /**
92 Implementation of Placeholder!T.
93 **/
94 class PlaceholderImpl(T) : Placeholder!T, Wrapper!T {
95 
96     private {
97         T payload;
98     }
99 
100     /**
101     Constructor for placeholder accepting stored value.
102 
103     Params:
104         value = value that will be stored in.
105     **/
106     this(ref T value) {
107         this.value = value;
108     }
109 
110     /**
111     ditto
112     **/
113     this(T value) {
114         this(value);
115     }
116 
117     /**
118     Get the type of component that is stored in.
119 
120     Returns:
121         TypeInfo of stored component.
122     **/
123     TypeInfo type() const {
124         return typeid(T);
125     }
126 
127     @property {
128         /**
129         Set value
130 
131         Params:
132             value = value to be stored
133 
134         Returns:
135             value
136         **/
137         ref T value(ref T value) @safe nothrow {
138             this.payload = value;
139 
140             return this.payload;
141         }
142 
143         /**
144         Get value
145 
146         Returns:
147             ref inout(T)
148         **/
149         ref inout(T) value() @safe nothrow inout @nogc {
150             return this.payload;
151         }
152     }
153 }
154 
155 /**
156 Wrap up a value in a placeholding object.
157 
158 Params:
159     value = value that will be wrapped in placeholding object
160     allocator = allocator used to allocate placeholding object
161 
162 Returns:
163     Placeholder!T with value as content.
164 **/
165 auto placeholder(T)(auto ref T value, RCIAllocator allocator = theAllocator) {
166     static if (is(T : Object)) {
167         return value;
168     } else {
169         return allocator.make!(PlaceholderImpl!T)(value);
170     }
171 }
172 
173 /**
174 Downcast object to type T, or unwrap it from Placeholder!T.
175 
176 Downcast object to type T, or unwrap it from Placeholder!T.
177 If T is rooted in Object, downcast will be performed, otherwise
178 it is assumed that T is stored in Placeholder!T object, and therefore
179 object is downcast to Placeholder!T and then returned.
180 
181 Params:
182     object = object from which to unwrap value of type T
183 Returns:
184     T if it is rooted in Object, or Placeholder!T if it is not.
185 **/
186 auto unwrap(T)(inout(Object) object) nothrow {
187     import aermicioi.aedi_property_reader.core.traits : n;
188     static if (is(T : Object)) {
189 
190         return cast(T) object;
191     } else {
192 
193         auto wrapper = (cast(Placeholder!T) object);
194 
195         assert(wrapper !is null, text(object.classinfo, " does not implement ", typeid(Placeholder!T), " ", typeid(T), " content cannot be extracted")).n;
196 
197         return wrapper;
198     }
199 }
200 
201 /**
202 Identify the type of object.
203 
204 Identify the type of object.
205 The object will be downcasted to TypeAware first, in order
206 to test if object is holding information about some type.
207 If so, the type from TypeAware object will be returned,
208 otherwise classinfo of object itself.
209 
210 Params:
211     object = component to be identified
212 Returns:
213     TypeInfo of stored type of object implements TypeAware, or classinfo of object itself.
214 **/
215 TypeInfo identify(T : Object)(in T object) nothrow @nogc {
216     if (object is null) {
217         return typeid(null);
218     }
219 
220     TypeAware p = cast(TypeAware) object;
221 
222     if (p !is null) {
223         return p.type;
224     }
225 
226     return object.classinfo;
227 }
228 
229 /**
230 Get the type of component.
231 
232 Params:
233     component = the component for which to return the type.
234 
235 Returns:
236     TypeInfo the type of component
237 **/
238 TypeInfo identify(T)(in T component) nothrow @nogc @safe
239     if (!is(T : Object)) {
240 
241     return typeid(component);
242 }