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 }