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