The OpenD Programming Language

1 /++
2 Conversion utilities.
3 
4 License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
5 Authors: Ilia Ki
6 +/
7 module mir.conv;
8 
9 import mir.exception: toMutable;
10 public import core.lifetime: emplace;
11 
12 import std.traits;
13 
14 private template isMirString(T)
15 {
16     static if (isSomeString!T)
17     {
18         enum isMirString = true;
19     }
20     else
21     {
22         static if (__traits(compiles, {import mir.small_string;}))
23         {
24             import mir.small_string;
25             enum isMirString = is(T : SmallString!size, size_t size);
26         }
27         else
28         {
29             enum isMirString = false;
30         }
31     }
32 }
33 
34 /++
35 The `to` template converts a value from one type _to another.
36 The source type is deduced and the target type must be specified, for example the
37 expression `to!int(42.0)` converts the number 42 from
38 `double` _to `int`. The conversion is "unsafe", i.e.,
39 it does not check for overflow.
40 +/
41 template to(T)
42 {
43     ///
44     auto ref T to(A...)(auto ref A args)
45         if (A.length > 0)
46     {
47         import mir.utility;
48         import mir.functional: forward;
49         static if (A.length == 1 && isImplicitlyConvertible!(A[0], T))
50             return args[0];
51         else
52         static if (is(T == class) && is(typeof(new T(forward!args))))
53             return new T(forward!args);
54         else
55         static if (is(typeof(T(args))))
56             return T(forward!args);
57         else
58         static if (A.length == 1)
59         {
60             alias I = A[0];
61             alias arg = args[0];
62             static if (is(typeof(cast(T) arg)) && !(isDynamicArray!T && isDynamicArray!I) && !isSomeString!T)
63                 return cast(T) forward!arg;
64             else
65             static if (isSomeString!I && is(T == enum))
66             {
67                 import mir.enums;
68                 uint index = void;
69                 if (getEnumIndexFromKey!T(arg, index)._expect(true))
70                     return index.unsafeEnumFromIndex!T;
71                 static immutable msg = "Can not convert string to the enum " ~ T.stringof;
72                 version (D_Exceptions)
73                 {
74                     static immutable Exception exc = new Exception(msg);
75                     throw exc.toMutable;
76                 }
77                 else
78                 {
79                     assert(0, msg);
80                 }
81             }
82             else
83             static if (is(I == enum) && isSomeString!T)
84             {
85                 import mir.enums;
86                 uint id = void;
87                 if (getEnumIndex(arg, id)._expect(true))
88                     return enumStrings!I[id];
89                 assert(0);
90             }
91             else
92             static if (isMirString!I && !isSomeString!T)
93             {
94                 static assert (__traits(compiles, { import mir.parse: fromString; }));
95                 import mir.parse: fromString;
96                 return fromString!(Unqual!T)(arg[]);
97             }
98             else
99             static if (!isSomeString!I && isMirString!T)
100             {
101                 // static if (is(Unqual!I == typeof(null)))
102                 // {
103                 //     enum immutable(T) ret = "null";
104                 //     static if (isImplicitlyConvertible!(immutable T, T))
105                 //         return ret;
106                 //     else
107                 //         return .to!T(ret[]);
108                 // }
109                 // else
110                 static if (is(Unqual!I == bool))
111                 {
112                     enum immutable(T) t = "true";
113                     enum immutable(T) f = "false";
114                     auto ret = arg ? t : f;
115                     static if (isImplicitlyConvertible!(immutable T, T))
116                         return ret;
117                     else
118                         return .to!T(ret[]);
119                 }
120                 else
121                 {
122                     static if (isImplicitlyConvertible!(T, string) && __traits(compiles, () {const(char)[] s =  arg.toString; return s;}))
123                     {
124                         auto ret = arg.toString;
125                         static if (is(typeof(ret) == string))
126                             return ret;
127                         else
128                             return ret.idup;
129                     }
130                     else
131                     {
132                         static assert (__traits(compiles, { import mir.format: print; }));
133                         import mir.format: print;
134                         static if (isSomeString!T)
135                         {
136                             static if (isNumeric!I)
137                             {
138                                 import mir.appender: UnsafeArrayBuffer;
139                                 alias C = Unqual!(ForeachType!T);
140                                 C[64] array = void;
141                                 auto buffer = UnsafeArrayBuffer!C(array);
142                             }
143                             else
144                             {
145                                 import mir.appender: scopedBuffer;
146                                 auto buffer = scopedBuffer!(Unqual!(ForeachType!T));
147                             }
148                             buffer.print(arg);
149                             static if (isMutable!(ForeachType!(T)))
150                                 return buffer.data.dup;
151                             else
152                                 return buffer.data.idup;
153                         }
154                         else
155                         {
156                             Unqual!T buffer;
157                             buffer.print(arg);
158                             return buffer;
159                         }
160                     }
161                 }
162             }
163             else
164             static if (is(I : const(C)[], C) && is(T : immutable(C)[]))
165             {
166                 static if (is(I : immutable(C)[]))
167                     return arg;
168                 else
169                     return idup(arg);
170             }
171             else
172             static if (is(I : const(D)[], D) && is(T : D[]))
173             {
174                 static if (is(I : D[]))
175                     return arg;
176                 else
177                     return dup(arg);
178             }
179             else 
180                 static assert(0, T.stringof);
181         }
182         else
183             static assert(0, T.stringof);
184     }
185 }
186 
187 ///
188 @safe pure @nogc
189 version(mir_core_test) unittest
190 {
191     enum E
192     {
193         A,
194         B,
195         C,
196     }
197 
198     assert(to!E("B") == E.B);
199     assert(to!string(E.B) == "B");
200     assert(to!string(null) is null);
201     assert(to!string(true) == "true");
202     assert(to!string(false) == "false");
203 
204     enum S : wstring
205     {
206         a = "A",
207         b = "B",
208     }
209 
210     assert(to!wstring(S.b) == "B"w);
211     assert(to!S("B"w) == S.b);
212 }
213 
214 /++
215 Emplace helper function.
216 +/
217 void emplaceInitializer(T)(scope ref T chunk) @trusted pure nothrow
218 
219 {
220     // Emplace T.init.
221     // Previously, an immutable static and memcpy were used to hold an initializer.
222     // With improved unions, this is no longer needed.
223     union UntypedInit
224     {
225         T dummy;
226     }
227     static struct UntypedStorage
228     {
229         align(T.alignof) void[T.sizeof] dummy;
230     }
231 
232     () @trusted {
233         *cast(UntypedStorage*) &chunk = cast(UntypedStorage) UntypedInit.init;
234     } ();
235 }
236 
237 /++
238 +/
239 T[] uninitializedFillDefault(T)(return scope T[] array) nothrow @nogc
240 {
241     static if (__VERSION__ < 2083)
242     {
243         static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1))
244         {
245             import core.stdc.string : memset;
246             memset(array.ptr, 0xff, T.sizeof * array.length);
247             return array;
248         }
249         else
250         {
251             pragma(inline, false);
252             foreach(ref e; array)
253                 emplaceInitializer(e);
254             return array;
255         }
256     }
257     else
258     {
259         static if (__traits(isZeroInit, T))
260         {
261             import core.stdc.string : memset;
262             memset(array.ptr, 0, T.sizeof * array.length);
263             return array;
264         }
265         else static if (__traits(isIntegral, T) && 0 == cast(T) (T.init + 1))
266         {
267             import core.stdc.string : memset;
268             memset(array.ptr, 0xff, T.sizeof * array.length);
269             return array;
270         }
271         else
272         {
273             pragma(inline, false);
274             foreach(ref e; array)
275                 emplaceInitializer(e);
276             return array;
277         }
278     }
279 }
280 
281 ///
282 pure nothrow @nogc @system
283 version(mir_core_test) unittest
284 {
285     static struct S { int x = 42; @disable this(this); }
286 
287     int[5] expected = [42, 42, 42, 42, 42];
288     S[5] arr = void;
289     uninitializedFillDefault(arr);
290     assert((cast(int*) arr.ptr)[0 .. arr.length] == expected);
291 }
292 
293 ///
294 @system 
295 version(mir_core_test) unittest
296 {
297     int[] a = [1, 2, 4];
298     uninitializedFillDefault(a);
299     assert(a == [0, 0, 0]);
300 }
301 
302 /++
303 Destroy structs and unions usnig `__xdtor` member if any.
304 Do nothing for other types.
305 +/
306 void xdestroy(T)(scope T[] ar)
307 {
308     static if ((is(T == struct) || is(T == union)) && __traits(hasMember, T, "__xdtor"))
309     {
310         static if (__traits(isSame, T, __traits(parent, ar[0].__xdtor)))
311         {
312             pragma(inline, false);
313             foreach_reverse (ref e; ar)
314                 e.__xdtor();
315         }
316     }
317 }
318 
319 ///
320 nothrow @nogc version(mir_core_test) unittest
321 {
322     __gshared int d;
323     __gshared int c;
324     struct D { ~this() nothrow @nogc {d++;} }
325     extern(C++)
326     struct C { ~this() nothrow @nogc {c++;} }
327     C[2] carray;
328     D[2] darray;
329     carray.xdestroy;
330     darray.xdestroy;
331     assert(c == 2);
332     assert(d == 2);
333     c = 0;
334     d = 0;
335 }
336 
337 
338 template emplaceRef(T)
339 {
340     void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
341     {
342         import core.lifetime: forward;
343         import core.internal.lifetime: emplaceRef;
344         return emplaceRef!T(chunk, forward!args);
345     }
346 }
347 
348 void emplaceRef(UT, Args...)(ref UT chunk, auto ref Args args)
349 if (is(UT == Unqual!UT))
350 {
351     import core.lifetime: forward;
352     emplaceRef!UT(chunk, forward!args);
353 }